О чем этот пример
При разработке игр часто возникает необходимость работать не с одиночными спрайтами, а с целыми группами однотипных объектов: пулями, врагами, бонусами. Ручное создание и управление физикой для каждого из них — утомительно и неэффективно. Phaser предлагает мощный инструмент — `PhysicsGroup`. Эта статья на практическом примере покажет, как создавать физические группы частиц (`balls`) и ящиков (`crates`), задавать их начальные свойства, случайным образом распределять по сцене и настраивать реакцию на столкновения между группами. Вы научитесь управлять десятками объектов несколькими строками кода.
Версия 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.image('ball', 'assets/sprites/shinyball.png');
this.load.image('crate', 'assets/sprites/crate32.png');
}
create ()
{
// Create a few balls
const balls = this.physics.add.group({
key: 'ball',
quantity: 24,
bounceX: 1,
bounceY: 1,
collideWorldBounds: true,
velocityX: 300,
velocityY: 150
});
// Create a few crates
const crates = this.physics.add.group({
key: 'crate',
quantity: 24,
bounceX: 1,
bounceY: 1,
collideWorldBounds: true,
velocityX: -150,
velocityY: -300
});
Phaser.Actions.RandomRectangle(balls.getChildren(), this.physics.world.bounds);
Phaser.Actions.RandomRectangle(crates.getChildren(), this.physics.world.bounds);
this.physics.add.collider(
balls,
crates,
(ball, crate) =>
{
ball.setAlpha(0.5);
crate.setAlpha(0.5);
});
}
}
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);
Создание физических групп с начальными свойствами
Вместо ручного создания каждого спрайта и настройки его физического тела, Phaser позволяет создать целую группу с помощью this.physics.add.group(). Этот метод принимает объект конфигурации, который определяет общие свойства для всех членов группы.
Ключевые параметры конфигурации:
* key: текстура для спрайтов в группе.
* quantity: количество создаваемых объектов.
* bounceX, bounceY: коэффициент упругости (отскока) по осям.
* collideWorldBounds: будут ли объекты сталкиваться с границами мира.
* velocityX, velocityY: начальная скорость по осям для каждого объекта.
const balls = this.physics.add.group({
key: 'ball',
quantity: 24,
bounceX: 1,
bounceY: 1,
collideWorldBounds: true,
velocityX: 300,
velocityY: 150
});
В данном примере создаются две группы: 24 блестящих шара и 24 ящика. Обратите внимание, что группам заданы противоположные начальные скорости, что сразу создает динамику на сцене.
Случайное размещение объектов по области
Созданные группы изначально находятся в одной точке. Чтобы распределить их по игровому полю, используется хелпер Phaser.Actions.RandomRectangle(). Этот метод принимает массив объектов (детей группы) и прямоугольную область (Phaser.Geom.Rectangle), внутри которой будут случайным образом размещены их координаты.
Получить всех детей группы можно методом .getChildren().
Phaser.Actions.RandomRectangle(balls.getChildren(), this.physics.world.bounds);
Phaser.Actions.RandomRectangle(crates.getChildren(), this.physics.world.bounds);
Здесь в качестве области используется this.physics.world.bounds — границы физического мира, которые обычно совпадают с размерами сцены. Каждый шар и ящик получит уникальные случайные координаты в пределах видимой области.
Настройка коллизий между группами
Самое мощное в использовании групп — это возможность настроить взаимодействие между ними одной инструкцией. Метод this.physics.add.collider() регистрирует обработчик столкновений между двумя объектами или группами.
this.physics.add.collider(
balls,
crates,
(ball, crate) =>
{
ball.setAlpha(0.5);
crate.setAlpha(0.5);
});
Первые два аргумента — это сами группы. Движок Arcade Physics автоматически проверит столкновения между каждым шаром и каждым ящиком.
Третий аргумент — опциональная callback-функция, которая выполняется в момент столкновения любой пары объектов из этих групп. В примере при столкновении шара и ящика их прозрачность (alpha) уменьшается до 0.5. Функция получает в качестве параметров конкретные объекты (ball, crate), которые столкнулись.
Конфигурация игры и сцены
Весь описанный функционал работает благодаря правильной настройке игры. Ключевой момент — активация физического движка Arcade в конфигурации игры.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade', // Используем Arcade Physics
arcade: {
debug: false // Отключаем отладочную визуализацию
}
},
scene: Example // Указываем наш класс сцены
};
const game = new Phaser.Game(config);
Класс сцены (Example) содержит методы preload() для загрузки ассетов и create() для инициализации игровых объектов и логики, которую мы разбирали выше.
Что попробовать дальше
Группы в Arcade Physics — это фундаментальный инструмент для оптимизации и структурирования кода при работе с множеством однотипных физических объектов. Они избавляют от рутины, позволяя управлять свойствами, размещением и взаимодействием десятков спрайтов как единым целым.
Для экспериментов попробуйте:
1. Изменить параметры групп (упругость, скорость, количество).
2. Использовать другую область для случайного размещения, например, только левую половину экрана.
3. В коллайдере изменить свойства объектов более радикально (например, setTint(0xff0000) для окрашивания или disableBody() для временного отключения физики).
4. Добавить третью группу и настроить коллизии между всеми тремя.
