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

В разработке игр точное позиционирование объектов — это залог стабильной физики, корректных коллизий и чистого визуального отображения. Особенно это критично при работе с пиксель-арт графикой или привязке объектов к сетке. В этом примере мы рассмотрим, как метод `Phaser.Geom.Rectangle.Floor()` помогает избавиться от "дрожания" и артефактов, округляя координаты прямоугольника до целых чисел. Метод `floor` (пол) — это математическая операция округления в меньшую сторону. Применяя её к координатам геометрической фигуры, мы гарантируем, что её положение на экране будет целочисленным, что часто необходимо для точного рендеринга. Визуальный пример наглядно демонстрирует разницу между обычным движением и движением с принудительным округлением.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    y = 0;
    graphics;
    rect2;
    rect1;

    create ()
    {
        this.graphics = this.add.graphics({ fillStyle: { color: 0x0000aa } });

        this.rect1 = new Phaser.Geom.Rectangle(100, 0, 250, 100);
        this.rect2 = new Phaser.Geom.Rectangle(450, 0, 250, 100);
    }

    update ()
    {
        this.y += 0.05;

        this.rect1.y = this.rect2.y = this.y;

        Phaser.Geom.Rectangle.Floor(this.rect2);

        this.graphics.clear();
        this.graphics.fillRectShape(this.rect1);
        this.graphics.fillRectShape(this.rect2);
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и создание объектов

В начале кода мы создаём класс сцены и объявляем необходимые переменные. Обратите внимание, что rect1 и rect2 будут хранить объекты прямоугольников, а graphics — это холст для их отрисовки.

class Example extends Phaser.Scene
{
    y = 0;
    graphics;
    rect2;
    rect1;

В методе create() инициализируются графический объект и два прямоугольника с помощью конструктора Phaser.Geom.Rectangle. Каждому задаётся начальная позиция `x,yи размерыwidth,height. Оба прямоугольника стартуют сy = 0`, но на разных горизонтальных позициях.

create ()
    {
        this.graphics = this.add.graphics({ fillStyle: { color: 0x0000aa } });

        this.rect1 = new Phaser.Geom.Rectangle(100, 0, 250, 100);
        this.rect2 = new Phaser.Geom.Rectangle(450, 0, 250, 100);
    }

Анимация и применение метода Floor

Сердцевина примера находится в методе update(), который вызывается каждый кадр. Здесь происходит движение прямоугольников вниз и ключевое действие — округление координат для одного из них.

update ()
    {
        this.y += 0.05;

        this.rect1.y = this.rect2.y = this.y;

        Phaser.Geom.Rectangle.Floor(this.rect2);

1. Переменная this.y увеличивается на небольшое значение (0.05) каждый кадр, создавая плавное движение. 2. Оба прямоугольника получают это новое значение координаты Y. На этом этапе rect1 и rect2 имеют идентичные дробные координаты (например, 5.15, 10.30 и т.д.). 3. Для прямоугольника rect2 вызывается статический метод Phaser.Geom.Rectangle.Floor(). Этот метод принимает объект прямоугольника в качестве аргумента и модифицирует его, округляя его свойства `x,y,widthиheight` до ближайших целых чисел в меньшую сторону (Math.floor).

Визуализация и результат

После расчёта позиций необходимо очистить холст и перерисовать прямоугольники, чтобы увидеть изменения.

this.graphics.clear();
        this.graphics.fillRectShape(this.rect1);
        this.graphics.fillRectShape(this.rect2);
    }
}

Метод this.graphics.clear() стирает всё, что было нарисовано в предыдущем кадре. Затем с помощью fillRectShape() оба прямоугольника отрисовываются заново.

В результате вы увидите, что левый прямоугольник (rect1) движется максимально плавно, но его пиксельные границы могут выглядеть размытыми или "дрожать" из-за субпиксельного рендеринга дробных координат. Правый прямоугольник (rect2) движется дискретно, "прилипая" к целым координатам. Его движение выглядит более резким, но границы всегда остаются чёткими, что исключает визуальные артефакты.

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

Стандартный блок конфигурации Phaser Game создаёт экземпляр игры с нашей сценой.

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

const game = new Phaser.Game(config);

Этот код создаёт игровое окно размером 800x600 пикселей с автоматическим выбором рендерера (WebGL или Canvas) и указывает наш класс Example в качестве основной сцены.

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

Метод Phaser.Geom.Rectangle.Floor — это простой, но мощный инструмент для контроля над отображением геометрических объектов. Он незаменим для задач, требующих жёсткого позиционирования по целым пикселям: создание тайловых карт, пиксель-арт анимаций или интерфейсов. **Идеи для экспериментов:** 1. Попробуйте применить метод Floor не только к rect2.y, но и к rect2.x, двигая прямоугольник по диагонали. 2. Сравните результат с методом Phaser.Geom.Rectangle.Ceil (округление в большую сторону) или просто с округлением Math.round. 3. Создайте спрайт и привяжите его положение к координатам округлённого прямоугольника, чтобы увидеть, как это влияет на движение игровых персонажей.