О чем этот пример

При создании игр часто возникает задача разместить множество объектов (врагов, бонусов, декораций) в определённой области не вручную, а случайным образом. Phaser предоставляет для этого простой и эффективный инструмент — `Phaser.Actions.RandomRectangle`. Эта статья покажет, как с его помощью одним вызовом заполнить прямоугольную зону сотнями спрайтов, что незаменимо для генерации уровней, создания партикловых эффектов или расстановки игровых элементов.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('orb', 'assets/sprites/orb-blue.png');
    }

    create ()
    {
        //  Create 300 sprites (they all start life at 0x0)
        const group = this.add.group({ key: 'orb', frameQuantity: 300 });

        const rect = new Phaser.Geom.Rectangle(300, 300, 300, 100);

        //  Randomly position the sprites within the rectangle
        Phaser.Actions.RandomRectangle(group.getChildren(), rect);
    }
}


const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Подготовка сцены и создание группы спрайтов

Вся магия происходит в методе create() нашей сцены. Первым делом нам нужно создать множество объектов, которые мы будем распределять. Вместо ручного создания каждого спрайта используется мощный механизм групп (Group).

const group = this.add.group({ key: 'orb', frameQuantity: 300 });

Этот код создаёт группу и сразу наполняет её 300 спрайтами, используя изображение 'orb', загруженное в preload(). Важный нюанс: все созданные спрайты изначально имеют координаты (0, 0) и накладываются друг на друга в верхнем левом углу холста.

Определение целевой области

Чтобы распределить спрайты, нужно задать границы области. Для этого создаётся объект Phaser.Geom.Rectangle.

const rect = new Phaser.Geom.Rectangle(300, 300, 300, 100);

Конструктор принимает четыре параметра: координаты X и Y левого верхнего угла прямоугольника, а затем его ширину и высоту. В нашем примере создаётся горизонтальная область шириной 300 пикселей и высотой 100 пикселей, начинающаяся в точке (300, 300).

Случайное распределение объектов

Самый важный шаг — вызов метода Phaser.Actions.RandomRectangle. Этот метод является частью статического класса Phaser.Actions, предназначенного для массовых операций над объектами.

Phaser.Actions.RandomRectangle(group.getChildren(), rect);

Метод принимает два аргумента. Первый — массив объектов для изменения. Мы получаем его через group.getChildren(). Второй — созданный нами прямоугольник rect. Внутри метод перебирает все переданные объекты и присваивает каждому случайные координаты `xиy`, которые гарантированно попадают внутрь заданного прямоугольника.

Как это работает под капотом?

После вызова RandomRectangle координаты `xиy` каждого спрайта в группе перезаписываются. Новые значения вычисляются по простой формуле: - x = rect.x + Math.random() * rect.width - y = rect.y + Math.random() * rect.height

Math.random() генерирует число от 0 до 1, которое масштабируется на ширину и высоту прямоугольника и прибавляется к его начальным координатам. Это обеспечивает равномерное случайное распределение по всей площади фигуры. Все изменения применяются мгновенно за один проход.

Что попробовать дальше

Phaser.Actions.RandomRectangle — это лаконичный и производительный способ заполнить игровую область объектами. Он идеален для создания полей астероидов, рощ деревьев, толп персонажей или облаков частиц. Для экспериментов попробуйте изменить размеры и положение прямоугольника, анимацию спрайтов после распределения или комбинируйте этот вызов с другими методами из Phaser.Actions, например, Call для назначения каждому объекту уникального поведения.