О чем этот пример
При разработке игр с физикой на Phaser отладочная визуализация — ваш главный помощник. Она показывает границы тел, их скорость и столкновения. Но когда объектов много, стандартные синие и белые контуры могут сливаться. В примере из официального репозитория Phaser демонстрируется мощный приём: назначение уникальных цветов для отладки разным типам физических тел. Это позволит вам моментально отличать друг от друга статические объекты, динамические тела разных категорий и даже круглые хитбоксы, что ускорит поиск багов в логике взаимодействий.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('gems', 'assets/tests/columns/gems.png', 'assets/tests/columns/gems.json');
this.load.image('mushroom', 'assets/sprites/mushroom2.png');
this.load.image('wizball', 'assets/sprites/wizball.png');
}
create ()
{
const spriteBounds = Phaser.Geom.Rectangle.Inflate(Phaser.Geom.Rectangle.Clone(this.physics.world.bounds), -100, -100);
this.anims.create({ key: 'diamond', frames: this.anims.generateFrameNames('gems', { prefix: 'diamond_', end: 15, zeroPad: 4 }), repeat: -1 });
this.anims.create({ key: 'prism', frames: this.anims.generateFrameNames('gems', { prefix: 'prism_', end: 6, zeroPad: 4 }), repeat: -1 });
this.anims.create({ key: 'ruby', frames: this.anims.generateFrameNames('gems', { prefix: 'ruby_', end: 6, zeroPad: 4 }), repeat: -1 });
this.anims.create({ key: 'square', frames: this.anims.generateFrameNames('gems', { prefix: 'square_', end: 14, zeroPad: 4 }), repeat: -1 });
this.physics.add.staticImage(400, 300, 'mushroom');
const anims = [ 'diamond', 'prism', 'ruby', 'square' ];
// Some normal dynamic bodies
for (let i = 0; i < 10; i++)
{
const anim = Phaser.Utils.Array.GetRandom(anims);
const pos = Phaser.Geom.Rectangle.Random(spriteBounds);
const block = this.physics.add.sprite(pos.x, pos.y, 'gems');
block.setVelocity(Phaser.Math.Between(-100, 100), Phaser.Math.Between(-100, 100));
block.setBounce(1).setCollideWorldBounds(true);
block.play(anim);
// Each type will have its own debug body color:
if (anim === 'diamond')
{
block.body.debugBodyColor = 0xadfefe;
}
else if (anim === 'prism')
{
block.body.debugBodyColor = 0x09b500;
}
else if (anim === 'ruby')
{
block.body.debugBodyColor = 0xb21d0a;
}
else if (anim === 'square')
{
block.body.debugBodyColor = 0x930ebe;
}
}
// A dynamic circular body
const ball = this.physics.add.image(100, 400, 'wizball').setCircle(46);
ball.setVelocity(Phaser.Math.Between(-100, 100), Phaser.Math.Between(-100, 100));
ball.setBounce(1).setCollideWorldBounds(true);
// A static circular body
this.physics.add.staticImage(500, 100, 'wizball').setCircle(46);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
debug: true,
debugShowBody: true,
debugShowStaticBody: true,
debugShowVelocity: true,
debugVelocityColor: 0xffff00,
debugBodyColor: 0x0000ff,
debugStaticBodyColor: 0xffffff
}
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и анимаций
В методе preload() загружаются необходимые ассеты: атлас спрайтов gems и отдельные изображения. Ключевая настройка происходит в create(). Сначала создаётся ограничивающая область spriteBounds на основе мировых границ физики (this.physics.world.bounds), уменьшенная на 100 пикселей с каждой стороны. Это гарантирует, что спавнящиеся объекты будут появляться в отдалении от краёв экрана.
Далее создаются четыре анимации на основе кадров из атласа gems. Каждая анимация (diamond, prism, ruby, square) будет проигрываться в цикле на соответствующих спрайтах.
const spriteBounds = Phaser.Geom.Rectangle.Inflate(Phaser.Geom.Rectangle.Clone(this.physics.world.bounds), -100, -100);
this.anims.create({ key: 'diamond', frames: this.anims.generateFrameNames('gems', { prefix: 'diamond_', end: 15, zeroPad: 4 }), repeat: -1 });
this.anims.create({ key: 'prism', frames: this.anims.generateFrameNames('gems', { prefix: 'prism_', end: 6, zeroPad: 4 }), repeat: -1 });
this.anims.create({ key: 'ruby', frames: this.anims.generateFrameNames('gems', { prefix: 'ruby_', end: 6, zeroPad: 4 }), repeat: -1 });
this.anims.create({ key: 'square', frames: this.anims.generateFrameNames('gems', { prefix: 'square_', end: 14, zeroPad: 4 }), repeat: -1 });
Создание тел и базовая настройка физики
В сцене создаются объекты разных типов. this.physics.add.staticImage добавляет статичное тело (гриб), которое не двигается при столкновениях. Затем в цикле генерируется 10 динамических тел (this.physics.add.sprite). Каждому случайным образом назначается одна из четырёх анимаций и позиция в пределах spriteBounds.
Каждое тело получает случайную начальную скорость и настройку упругости (setBounce(1)), а также включается отскок от границ мира. Это создаёт хаотичное движение объектов.
this.physics.add.staticImage(400, 300, 'mushroom');
const anims = [ 'diamond', 'prism', 'ruby', 'square' ];
for (let i = 0; i < 10; i++)
{
const anim = Phaser.Utils.Array.GetRandom(anims);
const pos = Phaser.Geom.Rectangle.Random(spriteBounds);
const block = this.physics.add.sprite(pos.x, pos.y, 'gems');
block.setVelocity(Phaser.Math.Between(-100, 100), Phaser.Math.Between(-100, 100));
block.setBounce(1).setCollideWorldBounds(true);
block.play(anim);
}
Кастомизация цветов для отладки
Самая важная часть примера — присвоение своего цвета отладки для каждого типа анимации. После создания и базовой настройки спрайта, в блоке if проверяется название его анимации. В зависимости от результата, свойству debugBodyColor физического тела (block.body) присваивается уникальное HEX-значение цвета.
Это свойство имеет приоритет над глобальным цветом debugBodyColor, заданным в конфигурации физики. Таким образом, все «бриллианты» станут бирюзовыми, «призмы» — зелёными, и так далее. Это мгновенно визуально группирует объекты.
if (anim === 'diamond')
{
block.body.debugBodyColor = 0xadfefe;
}
else if (anim === 'prism')
{
block.body.debugBodyColor = 0x09b500;
}
else if (anim === 'ruby')
{
block.body.debugBodyColor = 0xb21d0a;
}
else if (anim === 'square')
{
block.body.debugBodyColor = 0x930ebe;
}
Работа с круглыми хитбоксами
Пример также показывает работу с круглыми телами. Динамический шар (wizball) создаётся через this.physics.add.image. Метод .setCircle(46) заменяет стандартный прямоугольный хитбокс на круглый с радиусом 46 пикселей, что больше подходит для формы мяча. Ему также задаётся скорость и упругость.
Затем создаётся статический круглый объект из того же спрайта. Обратите внимание: для статического тела цвет отладки по умолчанию будет белым (debugStaticBodyColor: 0xffffff), как и задано в глобальной конфигурации, если не переопределить его индивидуально.
const ball = this.physics.add.image(100, 400, 'wizball').setCircle(46);
ball.setVelocity(Phaser.Math.Between(-100, 100), Phaser.Math.Between(-100, 100));
ball.setBounce(1).setCollideWorldBounds(true);
this.physics.add.staticImage(500, 100, 'wizball').setCircle(46);
Включение отладочного режима в конфиге
Вся отладочная визуализация работает только при явном включении отладки в настройках физического движка Arcade. В конфигурации игры необходимо установить debug: true, а также активировать отображение нужных элементов: тел, статических тел и векторов скорости. Здесь же задаются цвета по умолчанию, которые будут использоваться, если для конкретного тела не назначен свой debugBodyColor.
physics: {
default: 'arcade',
arcade: {
debug: true,
debugShowBody: true,
debugShowStaticBody: true,
debugShowVelocity: true,
debugVelocityColor: 0xffff00,
debugBodyColor: 0x0000ff,
debugStaticBodyColor: 0xffffff
}
}
Что попробовать дальше
Индивидуальная настройка цветов отладки — это простой, но чрезвычайно эффективный способ сделать процесс разработки и отладки физики в Phaser нагляднее. Вы можете назначать цвета не только по типу анимации, но и по группам, состоянию объекта (например, красный для повреждённых врагов) или по слою коллизий. Поэкспериментируйте: попробуйте динамически менять debugBodyColor при столкновениях, чтобы визуально фиксировать моменты контакта, или назначьте разные цвета для статических платформ с разными свойствами трения.
