О чем этот пример
При работе с 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-сцене.
