Строим дерево сцены для отрисовки
После переписывания рендеринга на Pixi.js, в планах было добавить возможность удобно писать кастомные шейдеры или быстро применять встроенные в Pixi фильтры вроде размытия на один или группу акторов. Повесить фильтр/шейдер на один объект несложно, а вот с группами возникла проблема.
Хоть сцена и акторы в движке представляют собой дерево, при отрисовке через Pixi я собирал все объекты для отображения в один плоский список. Вложенность при отрисовке мне была не нужна и так было проще сортировать спрайты по слоям и координатам чтобы те, что отображаются ниже на экране, перекрывали спрайты выше.
Переделать список на дерево для Pixi оказалось несложно, так как Pixi сцена тоже дерево, а для работы со слоями и сортировкой в последнем мажоре рендерера есть Layer API. Более серьезная проблема меня ожидала в текущей реализации компонента Transform.
Transform компонент отвечает за все трансформации актора в пространстве – смещение по координатам, поворот и масштабирование. Чтобы дочерние акторы учитывали смещение родителя, я в компоненте оставлял ссылку на такой же компонент родительского актора и считал общее смещение складывая родительские значения с дочерними. Это работало для очень простых сценариев, но если к примеру повернуть родителя, то дочерний объект повернется не относительно родителя как должно быть, а относительно своей оси.
Когда рендерер начал работать со сценой как с деревом, то появился рассинхрон между тем как объекты рисуются и как они воспринимаются остальными системами, которые работают с координатами объектов. Рендерер начал рисовать сцену правильно, а другие системы вроде физики продолжили использовать некорректно посчитанные координаты из Transform.
В результате я полностью переписал Transform компонент и начал использовать матрицы 3 на 3. Матрицы позволяют описать все преобразования по смещению, повороту и масштабированию объекта так, что при перемножении матриц дочернего объекта и родителя мы можем получить итоговые значения по всем преобразованиям.
Вся работа с матрицами спрятана под капот компонента, поэтому снаружи достаточно обращаться к привычным полям вида x, y, rotation и тд.
const transform = actor.getComponent(Transform);
// абсолютные координаты актора в мировом пространстве
console.log(transform.world.position.x, transform.world.position.y);
// относительно координаты актора в пространстве родителя
console.log(transform.local.position.x, transform.local.position.y);
Как обычно очередная фича в движке потребовала переработки того, что уже есть и было написано лишь бы работало, но теперь все подготовительные работы завершены, и можно приступать к поддержке кастомных шейдеров. Но это уже в следующем году.