О чем этот пример
При работе с физикой в играх важно понимать, как именно взаимодействуют объекты. Система физики Arcade в Phaser предоставляет два ключевых набора флагов: `touching` и `blocked`. Эта статья покажет, как их визуализировать и интерпретировать, что поможет вам точнее настраивать поведение объектов при столкновениях и отскоках, а также отлаживать сложные физические взаимодействия.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
text;
graphics;
block;
atari;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('block', 'assets/sprites/block.png');
this.load.image('flower', 'assets/sprites/flower-exo.png');
this.load.image('atari', 'assets/sprites/atari800.png');
}
create ()
{
this.atari = this.physics.add.image(400, 300, 'atari');
this.atari.body.setAllowGravity(false);
this.atari.body.setImmovable(true);
this.block = this.physics.add.image(0, 0, 'block');
this.block.setVelocity(200, 200);
this.block.setBounce(1, 1);
this.block.setCollideWorldBounds(true);
this.graphics = this.add.graphics();
this.text = this.add.text(0, 0, '-');
// During a Body vs. Body collision or overlap both become 'touching'.
// It's easier to see during an overlap:
this.physics.add.overlap(this.atari, this.block);
}
update ()
{
this.graphics.clear();
this.draw(this.atari);
this.draw(this.block);
this.text.setText([
'Box:',
'',
JSON.stringify(
Phaser.Utils.Objects.Pick(this.block.body, [ 'blocked', 'touching', 'embedded' ]),
null,
2
)
]);
}
draw (gameObject)
{
this.graphics.lineStyle(5, 0xffff00, 0.8);
this.drawEdges(gameObject.body, gameObject.body.touching);
this.graphics.lineStyle(5, 0xff0000, 0.8);
this.drawEdges(gameObject.body, gameObject.body.blocked);
}
drawEdges (body, edges)
{
if (edges.left)
{
this.graphics.lineBetween(body.left, body.top, body.left, body.bottom);
}
if (edges.up)
{
this.graphics.lineBetween(body.left, body.top, body.right, body.top);
}
if (edges.right)
{
this.graphics.lineBetween(body.right, body.top, body.right, body.bottom);
}
if (edges.down)
{
this.graphics.lineBetween(body.left, body.bottom, body.right, body.bottom);
}
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
gravity: { y: 200 }
}
},
scene: Example
};
const game = new Phaser.Game(config);
Суть флагов touching и blocked
В физическом движке Arcade каждый спрайт с телом (Body) имеет объекты touching и blocked. Оба содержат свойства для четырех сторон: up, down, left, right.
- **touching** указывает, какая грань тела *контактирует* с другим телом в данный момент. Этот флаг актуален как при простом касании, так и при наложении (overlap) объектов.
- **blocked** указывает, движение в каком направлении было *остановлено* другим телом или границей мира в течение последнего шага физики. Это ключевой флаг для обработки отскоков и блокировки движения.
В примере используется overlap вместо collider, чтобы контакт был постоянным и флаги touching всегда были видны.
Настройка сцены и физических тел
В методе create() создаются два основных спрайта с физикой и настраивается их поведение.
this.atari = this.physics.add.image(400, 300, 'atari');
this.atari.body.setAllowGravity(false);
this.atari.body.setImmovable(true);
Спрайт atari становится неподвижной статической преградой. Вызов setImmovable(true) означает, что при столкновении двигаться будет только другой объект, а atari останется на месте.
this.block = this.physics.add.image(0, 0, 'block');
this.block.setVelocity(200, 200);
this.block.setBounce(1, 1);
this.block.setCollideWorldBounds(true);
Спрайт block — динамическое тело. Оно получает начальную скорость, идеальный отскок (bounce: 1) и будет сталкиваться с границами холста.
this.physics.add.overlap(this.atari, this.block);
Регистрация перекрытия (overlap) между объектами. Это заставляет движок проверять их взаимодействие каждый кадр и обновлять флаги touching.
Визуализация граней в реальном времени
Ядро примера — метод draw(), который вызывается в update() для отрисовки линий по краям тел.
draw (gameObject)
{
this.graphics.lineStyle(5, 0xffff00, 0.8);
this.drawEdges(gameObject.body, gameObject.body.touching);
this.graphics.lineStyle(5, 0xff0000, 0.8);
this.drawEdges(gameObject.body, gameObject.body.blocked);
}
- **Желтым цветом (0xffff00)** рисуются грани, которые входят в контакт (touching).
- **Красным цветом (0xff0000)** рисуются грани, движение которых заблокировано (blocked).
Метод drawEdges() проверяет флаги в объекте edges и рисует линию вдоль соответствующей стороны bounding box тела.
if (edges.left)
{
this.graphics.lineBetween(body.left, body.top, body.left, body.bottom);
}
Вывод информации и интерпретация
В update() также обновляется текстовое поле, куда выводятся актуальные состояния тела движущегося блока.
this.text.setText([
'Box:',
'',
JSON.stringify(
Phaser.Utils.Objects.Pick(this.block.body, [ 'blocked', 'touching', 'embedded' ]),
null,
2
)
]);
Утилита Phaser.Utils.Objects.Pick создает новый объект, выбрав из тела блока только нужные свойства: blocked, touching и embedded. Это удобно для отладки.
Наблюдая за движением блока, вы увидите:
1. Когда блок касается неподвижного atari или границы мира, на этой грани загорится желтая линия (touching).
2. Если блок пытается двигаться в направлении, но встречает препятствие (например, прижимается к atari), на этой грани появится красная линия (blocked). При идеальном отскоке (bounce: 1) флаг blocked может мелькать очень быстро.
Что попробовать дальше
Визуализация touching и blocked дает четкое понимание того, как движок физики видит столкновения. Это мощный инструмент для отладки. Поэкспериментируйте: измените bounce на значение меньше 1, отключите setImmovable у atari или замените overlap на collider с callback-функцией. Посмотрите, как изменяется поведение флагов при разных типах взаимодействий.
