О чем этот пример
В стандартной настройке Arcade Physics все тела сталкиваются с границами игрового мира, которые совпадают с размерами сцены. Но что, если вам нужно ограничить движение объектов произвольной областью, например, экраном монитора или комнатой внутри уровня? В этой статье мы разберем, как задавать индивидуальные границы столкновений для физических тел, используя свойство `customBoundsRectangle`. Этот прием полезен для создания сложных игровых механик, где разные объекты должны взаимодействовать в своих собственных пространствах.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
// TODO rename <group custom world bounds.js>
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('monitor', 'assets/demoscene/monitor.png');
this.load.image('sky', 'assets/skies/space2.png');
this.load.spritesheet('ball', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
}
create ()
{
this.add.image(400, 300, 'sky');
// Balls in the default world bounds
const balls1 = this.physics.add.group({
key: 'ball',
frame: 1,
frameQuantity: 50,
bounceX: 1,
bounceY: 1,
collideWorldBounds: true,
velocityX: 100,
velocityY: 100
});
Phaser.Actions.RandomRectangle(balls1.getChildren(), this.physics.world.bounds);
this.add.image(400, 300, 'monitor');
// Balls in smaller bounds
const smallBounds = new Phaser.Geom.Rectangle(254, 186, 292, 210);
const balls2 = this.physics.add.group({
key: 'ball',
frame: 3,
frameQuantity: 50,
bounceX: 1,
bounceY: 1,
collideWorldBounds: true,
velocityX: 100,
velocityY: 100
});
for (const ball of balls2.getChildren())
{
ball.body.customBoundsRectangle = smallBounds;
}
Phaser.Actions.RandomRectangle(balls2.getChildren(), smallBounds);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
debug: false,
gravity: { y: 200 }
}
},
scene: Example
};
const game = new Phaser.Game(config);
Настройка сцены и загрузка ресурсов
В методе preload загружаются необходимые изображения. Обратите внимание на использование this.load.setBaseURL для указания базового URL-адреса ресурсов. Это упрощает загрузку ассетов из удаленного репозитория.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('monitor', 'assets/demoscene/monitor.png');
this.load.image('sky', 'assets/skies/space2.png');
this.load.spritesheet('ball', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
}
Спрайтшит ball загружается с указанием размеров кадра, что позволяет в дальнейшем выбирать конкретные кадры для разных групп объектов.
Стандартные границы мира и первая группа шаров
В методе create сначала добавляется фон, а затем создается первая группа физических шаров с помощью this.physics.add.group. Ключевой параметр collideWorldBounds: true заставляет тела сталкиваться с границами мира, которые по умолчанию равны размерам игры (в нашем случае 800x600).
const balls1 = this.physics.add.group({
key: 'ball',
frame: 1,
frameQuantity: 50,
bounceX: 1,
bounceY: 1,
collideWorldBounds: true,
velocityX: 100,
velocityY: 100
});
Шарам задается начальная скорость и коэффициент упругости (отскока) равный 1, что означает абсолютно упругое столкновение без потери энергии. Для случайного размещения объектов используется утилита Phaser.Actions.RandomRectangle, которая распределяет детей группы внутри переданного прямоугольника — в данном случае это this.physics.world.bounds.
Phaser.Actions.RandomRectangle(balls1.getChildren(), this.physics.world.bounds);
Создание пользовательских границ
Вторая группа шаров должна двигаться внутри меньшей области, имитируя экран монитора. Сначала определяется прямоугольник smallBounds, который задает новые границы.
const smallBounds = new Phaser.Geom.Rectangle(254, 186, 292, 210);
Затем создается вторая группа шаров (balls2). Обратите внимание, что для наглядности используется другой кадр спрайтшита (frame: 3). После создания группы, для каждого физического тела в цикле устанавливается свойство body.customBoundsRectangle.
for (const ball of balls2.getChildren())
{
ball.body.customBoundsRectangle = smallBounds;
}
Именно это свойство переопределяет глобальные границы мира для конкретного тела. Важно: параметр collideWorldBounds у группы все равно должен быть true. Изначальное размещение шаров также происходит внутри нового прямоугольника с помощью Phaser.Actions.RandomRectangle.
Phaser.Actions.RandomRectangle(balls2.getChildren(), smallBounds);
Конфигурация игры и физики
В конфигурационном объекте игры активируется Arcade Physics. Обратите внимание на параметр gravity: { y: 200 }. Несмотря на наличие гравитации, шары продолжают хаотично двигаться, так как им задана начальная скорость, а отскоки идеально упругие. Гравитация в данном примере влияет на траекторию, добавляя сложности движению.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
debug: false,
gravity: { y: 200 }
}
},
scene: Example
};
Запуск игры выполняется стандартным образом через создание экземпляра Phaser.Game.
Что попробовать дальше
Использование customBoundsRectangle открывает возможности для создания изолированных игровых зон, окон, порталов или безопасных областей. Вы можете экспериментировать: динамически менять границы во время игры в ответ на события, создавать несколько разных зон для различных типов объектов или комбинировать эту технику с системами камеры для сложных эффектов прокрутки.
