О чем этот пример
В играх часто возникает задача найти самый близкий к игроку врага, самый дальний ресурс или определить приоритетную цель. Ручной перебор объектов и расчет расстояний могут быть громоздкими. Пример демонстрирует, как встроенные методы физического движка Arcade `closest` и `furthest` решают эту задачу одной строкой кода, автоматически находя нужные объекты относительно заданной точки. Это мощный и производительный инструмент для реализации ИИ врагов, систем таргетинга или динамического взаимодействия с окружением.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
cursor;
graphics;
group;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('block', 'assets/sprites/block.png');
this.load.image('cursor', 'assets/sprites/drawcursor.png');
}
create ()
{
this.group = this.physics.add.group({
defaultKey: 'block',
bounceX: 1,
bounceY: 1,
collideWorldBounds: true
});
this.group.create(100, 200).setVelocity(100, 200);
this.group.create(500, 200).setVelocity(-100, -100);
this.group.create(300, 400).setVelocity(60, 100);
this.group.create(600, 300).setVelocity(-30, -50);
this.graphics = this.add.graphics();
this.cursor = this.add.image(400, 300, 'cursor');
this.input.on('pointermove', pointer =>
{
this.cursor.copyPosition(pointer);
});
}
update ()
{
const closest = this.physics.closest(this.cursor);
const furthest = this.physics.furthest(this.cursor);
this.graphics.clear()
.lineStyle(2, 0xff3300)
.lineBetween(closest.center.x, closest.center.y, this.cursor.x, this.cursor.y)
.lineStyle(2, 0x0099ff)
.lineBetween(furthest.center.x, furthest.center.y, this.cursor.x, this.cursor.y);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: { debug: false }
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и объектов
В методе preload загружаются два спрайта: блок и курсор. В create инициализируется физическая группа (PhysicsGroup). Группа настраивается с параметрами отскока и столкновения с границами мира, что сразу делает её объекты динамическими телами Arcade.
this.group = this.physics.add.group({
defaultKey: 'block',
bounceX: 1,
bounceY: 1,
collideWorldBounds: true
});
Затем в группу добавляются четыре спрайта блока в разных позициях, и каждому сразу задаётся начальная скорость. Это создаёт движущиеся объекты для наглядности.
this.group.create(100, 200).setVelocity(100, 200);
this.group.create(500, 200).setVelocity(-100, -100);
this.group.create(300, 400).setVelocity(60, 100);
this.group.create(600, 300).setVelocity(-30, -50);
Создаётся объект Graphics для отрисовки линий и спрайт курсора, который следует за указателем мыши, обновляя свою позицию по событию pointermove.
Магия методов closest и furthest
Вся логика поиска объектов заключена в методе update. Методы this.physics.closest() и this.physics.furthest() принимают в качестве аргумента целевую точку — в нашем случае это спрайт курсора (this.cursor). Движок автоматически перебирает все тела в текущем физическом мире Arcade (включая тела, созданные через группу) и находит среди них ближайшее и дальнее относительно центра переданного объекта.
const closest = this.physics.closest(this.cursor);
const furthest = this.physics.furthest(this.cursor);
Важно: методы возвращают не просто спрайт, а ссылку на физическое тело (Arcade Physics Body), что позволяет сразу обращаться к его свойствам, таким как center. Эти методы работают только при активном физическом движке Arcade.
Визуализация результата
Чтобы результат поиска был очевиден, от курсора к найденным объектам рисуются линии. Объект Graphics очищается каждый кадр, затем для ближайшего объекта рисуется красная линия, а для дальнего — синяя. Координаты для рисования берутся из центров физических тел (closest.center).
this.graphics.clear()
.lineStyle(2, 0xff3300)
.lineBetween(closest.center.x, closest.center.y, this.cursor.x, this.cursor.y)
.lineStyle(2, 0x0099ff)
.lineBetween(furthest.center.x, furthest.center.y, this.cursor.x, this.cursor.y);
Использование center тела, а не позиции спрайта, гарантирует, что линия будет проведена к точному центру массы объекта для физических расчётов, что особенно важно для объектов с нестандартным смещением (origin).
Конфигурация физического движка
Ключевой момент — активация физического движка Arcade в конфигурации игры. Без этого методы closest и furthest будут недоступны.
const config = {
type: Phaser.AUTO,
physics: {
default: 'arcade', // Движок должен быть 'arcade'
arcade: { debug: false }
},
scene: Example
};
Методы являются частью API менеджера физики (this.physics), который инициализируется именно при такой конфигурации. Они не будут работать с другими движками, например, Matter.js, без соответствующего плагина или реализации.
Что попробовать дальше
Методы closest и furthest — это мощные и лаконичные инструменты Phaser Arcade для пространственного анализа. Они избавляют разработчика от написания циклов и расчётов расстояний. Для экспериментов попробуйте
- передавать в методы не спрайт, а объект с координатами
{x, y} - использовать результат для автоматического наведения оружия врага на ближайшего игрока
- комбинировать поиск с фильтрацией, предварительно получая массив целей через
this.physics.overlap()и находя среди них самый близкий объект
