О чем этот пример
В играх часто нужны зоны взаимодействия, которые меняют размер во время выполнения. Например, портал, который пульсирует, или безопасная область, которая сужается. Встроенная зона (Zone) в Phaser статична, но мы можем заставить её динамически изменяться, обновляя хитбокс в реальном времени. В этой статье мы разберём пример с анимированной зоной, которая реагирует на наведение объектов и корректно обрабатывает коллизии даже при изменении размеров.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
create ()
{
const zone = this.add.zone(100, 100, 32, 32).setDropZone();
// Just a visual display of the drop zone
const graphics = this.add.graphics();
let color = 0xffff00;
graphics.lineStyle(2, color);
graphics.strokeRect(zone.x + zone.input.hitArea.x, zone.y + zone.input.hitArea.y, zone.input.hitArea.width, zone.input.hitArea.height);
this.input.setPollAlways();
// this.input.on('dragenter', function (pointer, gameObject, dropZone) {
this.input.on('gameobjectover', (pointer, gameObject, dropZone) =>
{
color = 0x00ffff;
});
// this.input.on('dragleave', function (pointer, gameObject, dropZone) {
this.input.on('gameobjectout', (pointer, gameObject, dropZone) =>
{
color = 0xffff00;
});
this.tweens.add({
targets: zone,
width: 400,
height: 200,
duration: 3000,
ease: 'Sine.easeInOut',
yoyo: true,
repeat: -1,
onUpdate: function ()
{
zone.setSize(zone.width, zone.height, true);
graphics.clear();
graphics.lineStyle(2, color);
graphics.strokeRect(zone.x + zone.input.hitArea.x, zone.y + zone.input.hitArea.y, zone.input.hitArea.width, zone.input.hitArea.height);
}
});
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Создание зоны и её визуализация
Сначала создаётся зона с помощью this.add.zone(). Важно сразу вызвать .setDropZone(), чтобы активировать её как цель для перетаскивания объектов. Поскольку зона сама по себе невидима, для отладки мы создаём графический объект (Graphics), который будет рисовать её контур.
const zone = this.add.zone(100, 100, 32, 32).setDropZone();
const graphics = this.add.graphics();
let color = 0xffff00;
graphics.lineStyle(2, color);
graphics.strokeRect(zone.x + zone.input.hitArea.x, zone.y + zone.input.hitArea.y, zone.input.hitArea.width, zone.input.hitArea.height);
Координаты для отрисовки берутся из zone.input.hitArea — это прямоугольник, который определяет область взаимодействия зоны относительно её позиции (zone.x, zone.y).
Реакция на наведение объектов
Чтобы зона меняла цвет при наведении на неё игрового объекта (например, перетаскиваемого спрайта), мы используем события ввода. Вместо устаревших dragenter/dragleave в примере применяются gameobjectover и gameobjectout. Они срабатывают, когда указатель с объектом входит в хитбокс зоны или выходит из него.
this.input.on('gameobjectover', (pointer, gameObject, dropZone) => {
color = 0x00ffff;
});
this.input.on('gameobjectout', (pointer, gameObject, dropZone) => {
color = 0xffff00;
});
Обратите внимание на this.input.setPollAlways(). Этот вызов гарантирует, что события ввода будут обрабатываться постоянно, даже если игра не в фокусе, что важно для плавной анимации и реакции.
Анимация изменения размера зоны
Сердце примера — твин, который плавно изменяет ширину и высоту зоны. Используется this.tweens.add() с параметрами для повторения и эффекта "йо-йо" (туда-обратно).
this.tweens.add({
targets: zone,
width: 400,
height: 200,
duration: 3000,
ease: 'Sine.easeInOut',
yoyo: true,
repeat: -1,
onUpdate: function () {
// Код обновления хитбокса и графики
}
});
Ключевой момент: изменение свойств zone.width и zone.height через твин не обновляет хитбокс автоматически. Хитбокс (zone.input.hitArea) остаётся прежним, и зона не будет корректно взаимодействовать с объектами.
Динамическое обновление хитбокса
Чтобы зона работала с новыми размерами, необходимо вручную обновить её хитбокс на каждом кадре анимации. Это делается в коллбэке onUpdate твина с помощью метода zone.setSize().
onUpdate: function () {
zone.setSize(zone.width, zone.height, true);
graphics.clear();
graphics.lineStyle(2, color);
graphics.strokeRect(zone.x + zone.input.hitArea.x, zone.y + zone.input.hitArea.y, zone.input.hitArea.width, zone.input.hitArea.height);
}
Метод setSize(width, height, updateOrigin) пересчитывает внутренний прямоугольник взаимодействия. Третий аргумент true означает, что система ввода (input.hitArea) также будет обновлена. После этого мы перерисовываем графический контур, используя обновлённые значения hitArea и текущий цвет.
Что попробовать дальше
Техника динамического обновления хитбокса через setSize() в onUpdate позволяет создавать в Phaser зоны с изменяемой геометрией. Это открывает возможности для механик с растущими опасными областями, пульсирующими точками интереса или адаптивными меню. Попробуйте заменить твин на изменение размера в ответ на игровые события (например, количество собранных предметов) или сделайте зону круглой, используя setCircle() вместо прямоугольного хитбокса.
