Обход дерева сцены

В PixiJS каждая сцена представляет собой иерархическую структуру объектов, где основной элемент — это Container. Контейнер может содержать как другие контейнеры, так и примитивные графические объекты (Sprite, Graphics, Text и др.). Такая иерархия образует дерево сцены, корнем которого обычно является экземпляр PIXI.Application.stage.

Каждый объект в дереве сцены обладает свойствами трансформации: position, scale, rotation, pivot и skew. Эти свойства наследуются вниз по дереву, что означает, что изменение контейнера влияет на все его дочерние элементы.

Методы обхода дерева сцены

PixiJS предоставляет несколько подходов для обхода дерева сцены и взаимодействия с его элементами:

1. Прямой обход через children

Каждый контейнер имеет массив children, который содержит все его прямые потомки. Цикл по этому массиву позволяет получить доступ к дочерним объектам.

const stage = app.stage;

stage.children.forEach(child => {
    console.log(child.name, child.position);
});

Важно помнить, что children включает только непосредственных потомков. Для работы с более глубокой иерархией потребуется рекурсивный подход.

2. Рекурсивный обход

Для обхода всех объектов дерева сцены используют рекурсивную функцию. Она позволяет выполнить действие на каждом элементе независимо от глубины вложенности.

function traverse(container, callback) {
    callback(container);
    if (container.children) {
        container.children.forEach(child => traverse(child, callback));
    }
}

traverse(app.stage, obj => {
    console.log(obj.name);
});

Этот метод универсален: можно выполнять любые действия, включая изменение свойств, удаление объектов или применение эффектов.

3. Использование встроенного метода getChildByName

Для поиска конкретного объекта по имени можно использовать getChildByName, однако стоит учитывать, что этот метод ищет только среди прямых детей контейнера. Для поиска во всем дереве сцены потребуется рекурсивный вызов:

function findByName(container, name) {
    if (container.name === name) return container;
    for (const child of container.children) {
        const result = findByName(child, name);
        if (result) return result;
    }
    return null;
}

const player = findByName(app.stage, 'player');

Изменение и управление объектами при обходе

Обход дерева сцены часто используется для:

  • Обновления свойств объектов: позиция, прозрачность, видимость.
  • Анимации: изменение координат или масштабов на основе времени.
  • Удаления объектов: рекурсивно удалять объекты с определённым условием.
  • Поиска и фильтрации: выбор объектов по имени, типу или кастомным свойствам.
traverse(app.stage, obj => {
    if (obj instanceof PIXI.Sprite) {
        obj.tint = 0xff0000; // перекрасить все спрайты в красный
    }
});

Оптимизация обхода

При большом количестве объектов в сцене важно учитывать производительность:

  • Ограничивать рекурсию только нужными контейнерами.
  • Кэшировать результаты поиска, если объекты не меняются часто.
  • Использовать visible и interactive свойства для игнорирования неактивных объектов.
  • Минимизировать лишние вычисления внутри цикла обхода.
function traverseVisible(container, callback) {
    if (!container.visible) return;
    callback(container);
    container.children.forEach(child => traverseVisible(child, callback));
}

Работа с событиями через дерево

Обход дерева сцены позволяет назначать события объектам без необходимости вручную подписываться на каждый элемент.

traverse(app.stage, obj => {
    if (obj.interactive) {
        obj.on('pointerdown', () => console.log(`${obj.name} clicked`));
    }
});

Это особенно полезно в интерфейсах с динамически создаваемыми объектами, когда заранее неизвестно, какие элементы будут присутствовать на сцене.

Комбинация обхода с фильтрацией

Для сложных сцен часто требуется сочетать рекурсивный обход с фильтрацией по типу или состоянию объекта:

traverse(app.stage, obj => {
    if (obj instanceof PIXI.Container && obj.name.startsWith('enemy')) {
        obj.alpha = 0.5; // сделать всех врагов полупрозрачными
    }
});

Фильтрация позволяет ограничить обработку только релевантными объектами, снижая нагрузку и упрощая код.

Практические советы

  • Присваивать объектам уникальные имена через свойство name для удобного поиска.
  • Использовать свойства контейнеров для группирования объектов по смыслу (например, uiContainer, enemiesContainer).
  • Для динамически создаваемых объектов поддерживать списки или карты, чтобы ускорить доступ без полного обхода дерева сцены.
  • При необходимости временного удаления объектов использовать removeChild и addChild вместо скрытия через visible, если требуется очистка памяти.

Обход дерева сцены является фундаментальным инструментом в PixiJS для управления визуальными элементами, оптимизации работы и реализации сложной логики. Правильное использование рекурсивного подхода, фильтрации и методов поиска обеспечивает гибкость и масштабируемость графических приложений.