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

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

Версия 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.Ceil(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);

Что делает Rectangle.Ceil?

Статический метод Phaser.Geom.Rectangle.Ceil() принимает объект прямоугольника в качестве аргумента и модифицирует его координаты `xиy, округляя их до ближайших больших целых чисел (по математической функцииMath.ceil`). Это изменяет сам переданный объект, а не создает новый.

// rect.x = 100.3, rect.y = 50.7
Phaser.Geom.Rectangle.Ceil(rect);
// После вызова: rect.x = 101, rect.y = 51

В примере из исходника мы видим два прямоугольника, rect1 и rect2. Их координата `yплавно увеличивается в каждом кадре, принимая дробные значения. Однако для второго прямоугольникаrect2каждый раз вызывается методCeil()`, что заставляет его "подпрыгивать" до целочисленной координаты, создавая эффект дискретного движения.

Разбор сцены: создание и анимация

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

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() реализована анимация. Координата `yувеличивается на небольшую дробную величину каждый кадр и присваивается обоим прямоугольникам. Затем кrect2применяетсяRectangle.Ceil()`, после чего оба прямоугольника перерисовываются.

update ()
{
    this.y += 0.05;
    this.rect1.y = this.rect2.y = this.y;
    Phaser.Geom.Rectangle.Ceil(this.rect2);
    this.graphics.clear();
    this.graphics.fillRectShape(this.rect1);
    this.graphics.fillRectShape(this.rect2);
}

Практическое применение в играх

Хотя пример демонстрирует просто визуальный эффект, у округления координат есть важные практические применения:

1. **Совмещение с тайловой картой:** При размещении спрайта на сетке тайлов (например, 32x32 пикселя) дробные координаты могут привести к тому, что спрайт будет отрисовываться между тайлами, вызывая размытие. Ceil() (или его аналоги Floor() и Round()) помогает "привязать" объект к узлам сетки. 2. **Физика и коллизии:** В некоторых простых физических системах или системах обнаружения столкновений на основе сетки работа с целыми координатами упрощает логику и делает ее более предсказуемой. 3. **Пиксель-перфект рендеринг:** Для сохранения четкости пиксель-арт графики при перемещении камеры или объектов часто требуется округление координат перед отрисовкой.

// Пример: привязка спрайта игрока к тайловой сетке
function snapToGrid(sprite, tileSize) {
    // Создаем временный прямоугольник на основе спрайта
    let bounds = new Phaser.Geom.Rectangle(sprite.x, sprite.y, sprite.width, sprite.height);
    // Округляем его координаты в большую сторону с учетом размера тайла
    bounds.x = Math.ceil(bounds.x / tileSize) * tileSize;
    bounds.y = Math.ceil(bounds.y / tileSize) * tileSize;
    // Применяем новые координаты к спрайту
    sprite.setPosition(bounds.x, bounds.y);
}

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

Метод Phaser.Geom.Rectangle.Ceil — это небольшой, но полезный инструмент для контроля над позиционированием геометрических объектов. Он помогает согласовать логику игры с визуальным представлением, особенно при работе с дискретными пространствами, такими как тайловые карты. Для экспериментов попробуйте заменить Ceil на Phaser.Geom.Rectangle.Floor (округление в меньшую сторону) или Phaser.Geom.Rectangle.Round (математическое округление) и понаблюдайте за разницей в движении. Также можно применить эти методы к координате `x` или одновременно к обеим осям, чтобы создать различные виды привязки объектов к игровому миру.