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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    a = 0;
    graphics;
    bounds;
    ellipse;

    create ()
    {
        this.graphics = this.add.graphics({ lineStyle: { width: 2, color: 0x0000aa }, fillStyle: { color: 0x00aaaa }});

        this.ellipse = new Phaser.Geom.Ellipse(400, 300, 250, 150);

        // if we omit the out parameter, we get a new Rectangle instance
        this.bounds = Phaser.Geom.Ellipse.GetBounds(this.ellipse);
    }

    update ()
    {
        this.a += 0.01;

        if (this.a > Math.PI * 4)
        {
            this.a -= Math.PI * 4;
        }

        this.ellipse.x = 400 - Math.cos(this.a / 2) * 300;
        this.ellipse.y = 300 - Math.sin(this.a * 2) * 200;
        this.ellipse.width = Math.sin(this.a) * Math.sin(this.a) * 300;
        this.ellipse.height = Math.cos(this.a) * Math.sin(this.a) * 300;

        // or we can supply a Rectangle instance to modify
        Phaser.Geom.Ellipse.GetBounds(this.ellipse, this.bounds);

        this.graphics.clear();

        this.graphics.fillEllipseShape(this.ellipse);

        this.graphics.strokeRectShape(this.bounds);
    }
}

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

const game = new Phaser.Game(config);

Инициализация сцены и создание эллипса

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

Сначала создаётся объект Graphics для рисования линий и заливки. Затем создаётся экземпляр Phaser.Geom.Ellipse. Его конструктор принимает координаты центра (x, y) и два радиуса (width, height).

this.graphics = this.add.graphics({ lineStyle: { width: 2, color: 0x0000aa }, fillStyle: { color: 0x00aaaa }});
this.ellipse = new Phaser.Geom.Ellipse(400, 300, 250, 150);

Важный момент — первое получение границ. Статический метод Phaser.Geom.Ellipse.GetBounds можно вызвать двумя способами. Если передать только эллипс, метод вернёт новый экземпляр Phaser.Geom.Rectangle.

this.bounds = Phaser.Geom.Ellipse.GetBounds(this.ellipse);

Динамическое изменение параметров эллипса

Логика анимации вынесена в метод update(), который выполняется каждый кадр. Угол `aпостоянно увеличивается, изменяя параметры эллипса: его положение (x,y) и размеры (width,height`).

this.a += 0.01;
if (this.a > Math.PI * 4)
{
    this.a -= Math.PI * 4;
}
this.ellipse.x = 400 - Math.cos(this.a / 2) * 300;
this.ellipse.y = 300 - Math.sin(this.a * 2) * 200;
this.ellipse.width = Math.sin(this.a) * Math.sin(this.a) * 300;
this.ellipse.height = Math.cos(this.a) * Math.sin(this.a) * 300;

Эти формулы создают сложное движение: центр эллипса движется по траектории Лиссажу, а размеры пульсируют по синусоидальным законам. После каждого изменения параметров эллипса необходимо пересчитать его ограничивающий прямоугольник.

Получение и использование ограничивающего прямоугольника

После обновления свойств эллипса вызывается метод GetBounds во второй раз. Здесь демонстрируется его вторая форма вызова — с передачей второго аргумента out. В этот аргумент нужно передать существующий объект Phaser.Geom.Rectangle, который будет изменён (модифицирован) новыми значениями. Это эффективно с точки зрения производительности, так как не создаёт новый объект каждый кадр.

Phaser.Geom.Ellipse.GetBounds(this.ellipse, this.bounds);

Затем graphics очищается, и происходит отрисовка. Сначала заливается сам эллипс, а поверх него рисуется контур его ограничивающего прямоугольника.

this.graphics.clear();
this.graphics.fillEllipseShape(this.ellipse);
this.graphics.strokeRectShape(this.bounds);

Визуально это позволяет наблюдать, как прямоугольная область точно охватывает эллипс при любых его деформациях и перемещениях.

Конфигурация и запуск игры

Код завершается стандартной для Phaser конфигурацией игры. В объекте config задаётся размер холста, тип рендерера, родительский HTML-элемент и основной класс сцены.

const config = {
    width: 800,
    height: 600,
    type: Phaser.AUTO,
    parent: 'phaser-example',
    scene: Example
};
const game = new Phaser.Game(config);

Именно эта конфигурация создаёт экземпляр игры, который запускает жизненный цикл сцены (create, update).

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

Метод Phaser.Geom.Ellipse.GetBounds — это ключевой инструмент для работы с пространственными границами эллипса. Его использование в двух формах (с созданием нового объекта или модификацией существующего) даёт гибкость для оптимизации. Для экспериментов попробуйте

  1. Использовать this.bounds для простой проверки пересечения с другими прямоугольниками
  2. Привязать изменение размеров эллипса к игровому времени
  3. Нарисовать несколько эллипсов с их bounds и реализовать отладочный режим их визуализации по нажатию клавиши