О чем этот пример

При работе с 3D объектами в Phaser, в частности с мешами (Mesh), разработчики иногда сталкиваются с ошибкой `mesh null currentbatch`. Эта ошибка возникает при попытке изменить свойства меша после его создания, если не соблюдены определенные условия. Чаще всего это происходит при использовании кастомных пайплайнов, таких как `Light2D`. Понимание причины и правильного порядка инициализации меша и его свойств сэкономит часы отладки и сделает вашу игру стабильнее. В этой статье мы разберем конкретный пример кода, который приводит к этой ошибке, и покажем, как его исправить, правильно настроив пайплайн и порядок загрузки данных для 3D модели.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload ()
    {
        // this.load.setBaseURL('https://cdn.phaserfiles.com/v385');
        this.load.setPath('https://labs.phaser.io/assets/obj/racing/');
        this.load.obj('roadStart', 'roadStartPositions.obj', 'roadStartPositions.mtl');
    }

    create ()
    {
        this.add.rectangle(50, 50, 50, 50, 0xff0000, 0.25).setPipeline('Light2D');

        const track = this.add.mesh(400, 300);

        track.setPipeline('Light2D');

        const rot90 = Phaser.Math.DegToRad(90);
        const rot180 = Phaser.Math.DegToRad(180);

        track.addVerticesFromObj('roadStart', 1, 0, 0, 0, rot90, rot180);

        track.setRotation(Math.random());
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#0a440a',
    parent: 'phaser-example',
    scene: Example
};

let game = new Phaser.Game(config);

Корень проблемы: порядок установки пайплайна

Основная причина ошибки mesh null currentbatch в предоставленном примере — попытка установить пайплайн Light2D для объекта Mesh до того, как у этого меша появятся вершины (вертексы). Пайплайн в Phaser тесно связан с процессом рендеринга (отрисовки) и требует наличия геометрии для работы.

В исходном коде вызов track.setPipeline('Light2D') происходит сразу после создания пустого меша, но до загрузки в него данных 3D-модели методом addVerticesFromObj. На этом этапе меш не имеет вершин, и его внутренняя структура для отрисовки (currentBatch) остается неинициализированной (null). Последующий вызов track.setRotation() пытается обновить состояние этого "пустого" объекта для рендеринга, что и приводит к ошибке.

const track = this.add.mesh(400, 300);
// Меш создан, но вершин у него пока нет.
track.setPipeline('Light2D'); // Установка пайплайна на "пустой" объект.
// ... загрузка вершин ...
track.setRotation(Math.random()); // Ошибка! currentbatch равен null.

Решение: правильная последовательность действий

Чтобы избежать ошибки, необходимо сначала наполнить меш геометрией (вершинами), и только после этого устанавливать для него нестандартный пайплайн или изменять свойства, влияющие на отрисовку. В случае с пайплайном Light2D его также можно (и часто нужно) установить для всей сцены через конфигурацию игры.

Вот исправленный порядок действий в методе create():

1. Создать меш. 2. Немедленно добавить в него вершины из загруженного OBJ-файла. 3. Теперь, когда меш содержит геометрию, для него можно безопасно установить кастомный пайплайн. 4. Изменять свойства вращения, позиции и т.д.

create ()
{
    // 1. Создаем меш
    const track = this.add.mesh(400, 300);

    // 2. Сначала добавляем вершины (геометрию)
    const rot90 = Phaser.Math.DegToRad(90);
    const rot180 = Phaser.Math.DegToRad(180);
    track.addVerticesFromObj('roadStart', 1, 0, 0, 0, rot90, rot180);

    // 3. Теперь безопасно устанавливаем пайплайн
    track.setPipeline('Light2D');

    // 4. Меняем свойства отрисовки
    track.setRotation(Math.random());
}

Глобальная настройка пайплайна Light2D

В примере также создается красный прямоугольник с полупрозрачностью, для которого также устанавливается пайплайн Light2D. Если в сцене несколько объектов должны использовать один и тот же пайплайн освещения, эффективнее настроить его глобально для рендерера. Это делается в конфигурации игры (config). Такой подход автоматически применяет пайплайн ко всем подходящим объектам сцены, упрощая код и потенциально повышая производительность.

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#0a440a',
    parent: 'phaser-example',
    scene: Example,
    pipeline: { 'Light2D': Phaser.Renderer.WebGL.Pipelines.Light2D } // Глобальная регистрация
};

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

// В методе create() после добавления вершин:
track.setPipeline('Light2D'); // Теперь пайплайн зарегистрирован в системе.

Работа с `addVerticesFromObj` и загрузкой ассетов

Метод addVerticesFromObj — это ключ к импорту 3D-геометрии из OBJ-файлов в 2D-пространство Phaser. Важно понимать его параметры, чтобы правильно позиционировать модель.

track.addVerticesFromObj(key, scale, x, y, z, rotateX, rotateY);

* key: Ключ, под которым OBJ-файл был загружен в preload() (в примере — 'roadStart'). * scale: Масштаб модели. * x, y, z: Смещение модели относительно позиции самого меша (track.x, track.y). * rotateX, rotateY: Углы поворота модели вокруг осей X и Y в радианах.

В примере файлы загружаются с удаленного сервера Labs. Для продакшена вам нужно будет разместить ассеты (roadStartPositions.obj и roadStartPositions.mtl) на своем сервере и указать корректный путь в this.load.setPath() или this.load.setBaseURL().

preload ()
{
    this.load.setPath('assets/models/racing/'); // Локальный путь
    this.load.obj('roadStart', 'roadStartPositions.obj', 'roadStartPositions.mtl');
}

Что попробовать дальше

Ошибка mesh null currentbatch — это надежный индикатор того, что вы пытаетесь работать с мешем как с объектом для рендеринга до того, как у него появилась геометрия. Главное правило: сначала addVerticesFromObj(), потом setPipeline() и другие манипуляции с отрисовкой. Дополнительно, для пайплайнов вроде Light2D, рассмотрите возможность их глобальной регистрации в конфигурации игры. **Идеи для экспериментов:** Попробуйте анимировать вращение меша в update(), изменять масштаб с помощью setScale, или добавить несколько мешей с разными моделями, управляя их положением относительно камеры. Также поэкспериментируйте с параметрами метода addVerticesFromObj, чтобы понять, как scale и углы поворота влияют на итоговое положение модели в 2D-сцене.