О чем этот пример
При работе с системой частиц в Phaser 3 вы можете столкнуться с неожиданным поведением зон смерти (Death Zone). В то время как зоны эмиссии (Emit Zone) используют локальные координаты эмиттера, зоны смерти оперируют глобальными координатами сцены. Эта статья поможет понять разницу и правильно позиционировать зоны смерти, избегая распространённой ошибки, когда частицы исчезают не там, где вы ожидаете.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class ParticlesSquare extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('flare', 'assets/particles/white-flare.png');
}
create ()
{
let particles = this.add.particles(300, 300, "flare", {
lifespan: 1500,
alpha: { start: 0, end: 1 },
scale: 0.2,
quantity: 20,
emitting: false,
})
// emit zone position is in local emitter space (as expected)
let emitZone = new Phaser.Geom.Circle(0, 0, 300)
particles.addEmitZone({
source: emitZone,
type: "random",
quantity: -1,
})
// deathZone position is in global space
let deathZone = new Phaser.Geom.Circle(0, 0, 200)
// you need to manually calculate the offset to put it in the right place
// let deathZone = new Phaser.Geom.Circle(particles.x, particles.y, 100)
particles.addDeathZone({
source: deathZone,
type: "onEnter",
})
particles.start(1500)
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: ParticlesSquare
};
const game = new Phaser.Game(config);
Проблема: зоны смерти работают в глобальных координатах
В примере создаётся эмиттер частиц в позиции (300, 300) сцены. Для него добавляются две зоны: зона эмиссии (Emit Zone) в форме круга и зона смерти (Death Zone), также в форме круга.
Ключевое отличие: координаты центра круга для зоны эмиссии (0, 0) интерпретируются относительно позиции самого эмиттера (300, 300). А вот координаты центра круга для зоны смерти (0, 0) берутся относительно начала координат всей сцены, то есть левого верхнего угла (0, 0). Это приводит к тому, что зона смерти оказывается не там, где вы её ожидаете увидеть.
let deathZone = new Phaser.Geom.Circle(0, 0, 200)
particles.addDeathZone({
source: deathZone,
type: "onEnter",
})
Решение: ручной расчёт позиции Death Zone
Чтобы зона смерти располагалась корректно относительно эмиттера, её координаты нужно задавать в глобальной системе отсчёта сцены. Поскольку эмиттер находится в точке (300, 300), центр зоны смерти тоже должен быть установлен в (300, 300).
Проще всего это сделать, создавая геометрию зоны смерти, используя позицию эмиттера (particles.x, particles.y). Это гарантирует, что зона будет находиться ровно под эмиттером.
// Корректное создание зоны смерти в глобальных координатах
let deathZone = new Phaser.Geom.Circle(particles.x, particles.y, 200)
particles.addDeathZone({
source: deathZone,
type: "onEnter",
})
Сравнение Emit Zone и Death Zone
Давайте чётко разграничим поведение двух типов зон, чтобы эта разница в системах координат не застала вас врасплох в будущем.
* **Emit Zone (Зона эмиссии)**: Определяет область, из которой появляются новые частицы. Координаты её геометрии (source) задаются в **локальном пространстве эмиттера**. Точка (0, 0) зоны соответствует позиции эмиттера на сцене.
* **Death Zone (Зона смерти)**: Определяет область, при входе в которую частицы уничтожаются. Координаты её геометрии (source) задаются в **глобальном пространстве сцены**. Точка (0, 0) зоны соответствует левому верхнему углу холста.
// Emit Zone: локальные координаты (0,0 = позиция эмиттера)
let emitZone = new Phaser.Geom.Circle(0, 0, 300)
particles.addEmitZone({
source: emitZone,
type: "random",
quantity: -1,
})
// Death Zone: глобальные координаты (0,0 = угол сцены)
let deathZone = new Phaser.Geom.Circle(particles.x, particles.y, 200)
particles.addDeathZone({
source: deathZone,
type: "onEnter",
})
Практический совет: выносите координаты в переменные
Чтобы код был более читаемым и устойчивым к изменениям, позицию эмиттера и радиусы зон лучше выносить в константы или переменные. Это особенно полезно, если вы планируете динамически перемещать эмиттер или менять размеры зон.
Такой подход делает настройки системы частиц более наглядными и позволяет легко их регулировать.
create ()
{
const emitterX = 300;
const emitterY = 300;
const deathZoneRadius = 200;
let particles = this.add.particles(emitterX, emitterY, "flare", {
// ... настройки частиц
});
// Создаем Death Zone с явно указанными глобальными координатами
let deathZone = new Phaser.Geom.Circle(emitterX, emitterY, deathZoneRadius);
particles.addDeathZone({
source: deathZone,
type: "onEnter",
});
}
Что попробовать дальше
Главный вывод: всегда помните о контексте системы координат при работе с addEmitZone и addDeathZone в Phaser. Для экспериментов попробуйте создать эмиттер, который следует за указателем мыши, и динамически обновляйте позицию его зоны смерти. Или реализуйте сложную систему с несколькими зонами смерти разной формы, которые включаются и выключаются по таймеру.
