О чем этот пример
При создании игр часто возникает задача генерировать объекты (врагов, бонусы, декорации) в определённой зоне, но с важным условием — они не должны появляться внутри запрещённых областей, например, внутри зданий или безопасных зон. Вручную рассчитывать такие позиции сложно и неэффективно. Метод `Phaser.Geom.Rectangle.RandomOutside` из Phaser 3 решает эту задачу элегантно. Он генерирует случайные точки внутри одного прямоугольника (`rectOuter`), но гарантированно вне другого (`rectInner`). Это мощный инструмент для процедурной генерации игрового пространства, который мы разберем на практическом примере.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
graphics;
rectInner;
rectOuter;
create ()
{
this.graphics = this.add.graphics({ lineStyle: { width: 1, color: 0x00ff00 }, fillStyle: { color: 0xff00ff }});
this.rectOuter = new Phaser.Geom.Rectangle(50, 100, 600, 450);
this.rectInner = new Phaser.Geom.Rectangle(250, 200, 300, 200);
this.plotRandomPoints();
this.time.addEvent({ delay: 1000, callback: () => this.plotRandomPoints, loop: true });
}
plotRandomPoints ()
{
this.graphics.clear();
this.graphics.strokeRectShape(this.rectOuter);
this.graphics.strokeRectShape(this.rectInner);
for (let i = 0; i < 400; i++)
{
const p = Phaser.Geom.Rectangle.RandomOutside(this.rectOuter, this.rectInner);
this.graphics.fillRect(p.x, p.y, 2, 2);
}
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и определение областей
В методе create() инициализируется сцена. Создаются два прямоугольника, которые определяют игровые зоны: внешняя граница (rectOuter) и внутренняя запрещенная область (rectInner).
Для визуализации используется объект Graphics. Он позволяет рисовать примитивы, такие как линии и залитые фигуры. В данном случае он настроен на рисование зеленых линий и заливку фиолетовым цветом.
this.graphics = this.add.graphics({ lineStyle: { width: 1, color: 0x00ff00 }, fillStyle: { color: 0xff00ff }});
this.rectOuter = new Phaser.Geom.Rectangle(50, 100, 600, 450);
this.rectInner = new Phaser.Geom.Rectangle(250, 200, 300, 200);
Генерация и отрисовка случайных точек
Основная логика заключена в методе plotRandomPoints(). Сначала холст Graphics очищается, и заново отрисовываются контуры обоих прямоугольников. Это нужно для обновления кадра.
Затем в цикле генерируются 400 точек. Ключевой вызов — Phaser.Geom.Rectangle.RandomOutside(this.rectOuter, this.rectInner). Этот статический метод возвращает объект точки (`p) со свойствамиxиy. Координаты точки всегда лежат внутриrectOuter, но за пределамиrectInner`.
Сгенерированная точка отрисовывается как маленький залитый прямоугольник размером 2x2 пикселя.
for (let i = 0; i < 400; i++)
{
const p = Phaser.Geom.Rectangle.RandomOutside(this.rectOuter, this.rectInner);
this.graphics.fillRect(p.x, p.y, 2, 2);
}
Автоматическое обновление с помощью таймера
Чтобы демонстрация была динамической, точки перерисовываются каждую секунду. Для этого используется событие таймера this.time.addEvent. Оно запускает функцию plotRandomPoints с заданной задержкой в 1000 миллисекунд (1 секунда) в бесконечном цикле.
Обратите внимание на синтаксис в исходном примере: в коллбек передается () => this.plotRandomPoints. Это стрелочная функция, которая вызывает метод. Если бы мы передали просто this.plotRandomPoints, метод вызвался бы один раз при создании события, а его результат (undefined) был бы передан как коллбек. Наш вариант — правильный.
this.time.addEvent({ delay: 1000, callback: () => this.plotRandomPoints(), loop: true });
Практическое применение в играх
В реальном проекте вместо отрисовки точек вы бы создавали игровые объекты. Например, можно генерировать врагов на уровне, но не внутри базы игрока, определенной как rectInner.
1. **Спавн врагов:** Передайте сгенерированные координаты в фабрику объектов.
const spawnPoint = Phaser.Geom.Rectangle.RandomOutside(levelBounds, safeZone);
this.enemies.create(spawnPoint.x, spawnPoint.y, 'enemy');
2. **Разброс ресурсов:** Создавайте бонусы или ресурсы в случайных местах карты, избегая непроходимых зон. 3. **Размещение декораций:** Заполняйте фон деревьями или камнями, оставляя чистыми пути для перемещения.
Метод работает именно с прямоугольниками. Для сложных форм потребуется разбить область на несколько прямоугольников или использовать другие геометрические методы Phaser.
Что попробовать дальше
Метод RandomOutside — это отличный пример того, как встроенная геометрическая библиотека Phaser упрощает решение распространенных игровых задач. Он избавляет от необходимости писать собственные, часто громоздкие, алгоритмы проверки и генерации.
Для экспериментов попробуйте:
* Изменить размеры и положение внутреннего прямоугольника, чтобы увидеть, как меняется распределение точек.
* Использовать полученные координаты для создания спрайтов из атласа вместо примитивов Graphics.
* Реализовать спавн мобов волнами, очищая старые точки и генерируя новые с каждой волной.
