О чем этот пример
При разработке игр часто возникает задача взаимодействия с группой объектов в определенной зоне: область активации способности, радиус взрыва, подсветка юнитов под курсором. Вручную проверять расстояния до каждого объекта в сцене — неэффективно. Встроенный метод физического движка Arcade `this.physics.overlapCirc()` решает эту проблему, возвращая массив физических тел, находящихся внутри заданного круга. Эта статья покажет, как использовать этот метод на практическом примере для создания динамической подсветки объектов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
circle;
sprites = [];
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('flower', 'assets/sprites/flower-exo.png');
this.load.image('mushroom', 'assets/sprites/mushroom16x16.png');
}
create ()
{
this.sprites = [];
// 240 Dynamic Bodies
for (let i = 0; i < 240; i++)
{
const pos = Phaser.Geom.Rectangle.Random(this.physics.world.bounds);
const block = this.physics.add.image(pos.x, pos.y, 'mushroom');
block.setBounce(1).setCollideWorldBounds(true);
Phaser.Math.RandomXY(block.body.velocity, 100);
this.sprites.push(block);
}
// 1 Static Body
this.sprites.push(this.physics.add.staticImage(400, 300, 'flower'));
this.circle = this.add.circle(400, 300, 150).setStrokeStyle(2, 0xffff00);
this.input.on('pointermove', (pointer) =>
{
this.circle.copyPosition(pointer);
});
}
update ()
{
Phaser.Actions.SetAlpha(this.sprites, 0.5);
const { x, y, radius } = this.circle;
const bodiesInCircle = this.physics.overlapCirc(x, y, radius, true, true);
Phaser.Actions.SetAlpha(bodiesInCircle.map(body => body.gameObject), 1);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
gravity: { y: 100 },
debug: false
}
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и создание тел
В методе preload загружаются два спрайта: 'mushroom' для динамических тел и 'flower' для статического. Это стандартная процедура.
В create сначала очищается массив this.sprites, который будет хранить все игровые объекты. Затем в цикле создается 240 динамических тел.
for (let i = 0; i < 240; i++)
{
const pos = Phaser.Geom.Rectangle.Random(this.physics.world.bounds);
const block = this.physics.add.image(pos.x, pos.y, 'mushroom');
block.setBounce(1).setCollideWorldBounds(true);
Phaser.Math.RandomXY(block.body.velocity, 100);
this.sprites.push(block);
}
Каждое тело размещается в случайной позиции в пределах мировых границ (this.physics.world.bounds). Метод setBounce(1) делает отскок абсолютно упругим, а setCollideWorldBounds(true) включает столкновение с границами мира. Phaser.Math.RandomXY задает телу случайный вектор скорости длиной 100 пикселей в секунду.
После цикла в массив добавляется одно статическое тело — цветок в центре экрана.
this.sprites.push(this.physics.add.staticImage(400, 300, 'flower'));
Затем создается графический объект circle, который будет визуально отображать зону поиска. Его позиция привязана к курсору мыши.
this.circle = this.add.circle(400, 300, 150).setStrokeStyle(2, 0xffff00);
this.input.on('pointermove', (pointer) =>
{
this.circle.copyPosition(pointer);
});
Принцип работы метода `overlapCirc`
Ключевая логика находится в методе update, который выполняется каждый кадр. Сначала всем спрайтам в массиве this.sprites устанавливается полупрозрачность (альфа = 0.5).
Phaser.Actions.SetAlpha(this.sprites, 0.5);
Затем из объекта this.circle извлекаются его текущие координаты центра и радиус. Эти параметры передаются в метод this.physics.overlapCirc().
const { x, y, radius } = this.circle;
const bodiesInCircle = this.physics.overlapCirc(x, y, radius, true, true);
Метод overlapCirc принимает пять аргументов:
1. `x,y` — координаты центра круга.
2. radius — радиус круга.
3. includeDynamic (true) — включать ли в результат динамические тела.
4. includeStatic (true) — включать ли в результат статические тела.
Метод возвращает массив объектов Arcade.Body, которые в данный момент пересекают заданную круговую область. Важно: проверяется пересечение именно физических тел (collision bodies), а не просто графических объектов.
В последней строке для всех найденных тел сбрасывается прозрачность (альфа = 1), чтобы они выделялись. Поскольку bodiesInCircle содержит тела, а не игровые объекты, используется map для получения связанных с ними gameObject.
Phaser.Actions.SetAlpha(bodiesInCircle.map(body => body.gameObject), 1);
Таким образом, объекты, попавшие в круг, подсвечиваются в реальном времени.
Конфигурация физического движка Arcade
Для работы примера необходима правильная настройка физики в конфигурации игры. Ключевой момент — активация движка Arcade.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade', // Используем движок Arcade Physics
arcade: {
gravity: { y: 100 }, // Необязательная гравитация
debug: false // Отключение отладочного режима
}
},
scene: Example
};
Указание default: 'arcade' делает этот движок доступным через this.physics в сцене. Параметр gravity добавляет вертикальное ускорение, что заставляет динамические тела падать, но в данном примере они также отскакивают от границ мира. debug: false скрывает визуальные контуры тел, которые полезны при отладке коллизий.
Практические применения и вариации
Метод overlapCirc — мощный инструмент для геймдизайна. Вот несколько идей для его использования:
* **Область поражения:** При взрыве найти все тела в радиусе и нанести им урон. * **Аура или бафф:** Постоянно проверять, какие игроки или юниты находятся в радиусе действия способности союзника. * **Выбор объектов:** Реализовать выделение группы юнитов с помощью мыши (как в RTS).
Для работы с другими формами используйте родственные методы:
overlapRect для прямоугольной области и overlap для проверки пересечения двух конкретных тел или групп.
Важно помнить, что метод проверяет физические тела. Если спрайт не имеет тела (не добавлен через this.physics.add), он не будет обнаружен. Для нефизических объектов потребуется ручная проверка расстояний или использование методов из Phaser.Geom.Circle.
Что попробовать дальше
Использование this.physics.overlapCirc() позволяет легко и производительно обрабатывать взаимодействие с объектами в заданной круговой области, что критически важно для многих игровых механик. Метод интегрирован в физический движок и работает с оптимизированными структурами данных, избавляя разработчика от написания циклов проверки расстояний.
**Идеи для экспериментов:**
1. Измените последние два параметра overlapCirc на (true, false) и посмотрите, как статический цветок перестанет подсвечиваться.
2. Добавьте обработчик клика (this.input.on('pointerdown')), который будет применять импульс (setVelocity) ко всем телам, найденным внутри круга.
3. Замените круг на прямоугольник, используя this.physics.overlapRect(x, y, width, height, ...), и создайте зону выделения, как в стратегиях.
