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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    y = 100;
    graphics;
    text2;
    text1;
    point2;
    point1;

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

        this.point1 = new Phaser.Math.Vector2(300, 100);
        this.point2 = new Phaser.Math.Vector2(500, 100);

        this.text1 = this.add.text(100, 50, '');
        this.text2 = this.add.text(500, 50, '');
    }

    update ()
    {
        this.y += 0.05;

        this.point1.y = this.point2.y = this.y;

        this.point2.floor();

        this.text1.setText(`y: ${this.point1.y}`);
        this.text2.setText(`y: ${this.point2.y}`);

        this.graphics.clear();
        this.graphics.fillPointShape(this.point1, 20);
        this.graphics.fillPointShape(this.point2, 20);
    }
}

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

const game = new Phaser.Game(config);

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

В начале кода мы создаем класс сцены Example. В нем объявляются несколько свойств: две текстовые метки (text1, text2), два вектора (point1, point2), объект для рисования (graphics) и переменная `y` для управления вертикальным движением.

В методе create() инициализируются основные объекты:

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

Здесь создается объект Graphics с синим цветом заливки, который будет использоваться для отрисовки точек.

Далее создаются два экземпляра вектора Phaser.Math.Vector2. Они представляют собой точки в 2D-пространстве с начальными координатами (300, 100) и (500, 100).

this.point1 = new Phaser.Math.Vector2(300, 100);
this.point2 = new Phaser.Math.Vector2(500, 100);

Также создаются две текстовые метки для отображения текущих координат Y каждой точки. Их начальный текст пустой, он будет обновляться в update().

Анимация и применение метода floor()

Основная логика анимации находится в методе update(), который вызывается каждый кадр.

Сначала увеличивается значение переменной `y` на небольшую величину (0.05). Это создает плавное вертикальное движение.

this.y += 0.05;

Затем это новое значение `yприсваивается свойству.y` обоих векторов. Таким образом, обе точки движутся синхронно вниз с одинаковой скоростью.

this.point1.y = this.point2.y = this.y;

Ключевой момент: к вектору point2 применяется метод floor().

this.point2.floor();

Метод floor() класса Phaser.Math.Vector2 округляет **каждую** компоненту вектора (X и Y) до ближайшего меньшего целого числа (математическая функция «пол»). В нашем примере координата X у point2 равна 500 (целое число), а координата Y — это значение this.y, которое с каждым кадром становится дробным (например, 100.05, 100.10 и т.д.). Метод floor() берет это дробное значение Y и превращает его, например, в 100, пока this.y не достигнет 101.

Визуализация и отображение данных

После обновления координат необходимо обновить текстовые метки, чтобы отобразить текущие значения Y для каждой точки. Для point1 мы показываем исходное дробное значение, для point2 — уже округленное методом floor().

this.text1.setText(`y: ${this.point1.y}`);
this.text2.setText(`y: ${this.point2.y}`);

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

this.graphics.clear();

Наконец, рисуются сами точки в виде залитых кругов с радиусом 20 пикселей в текущих координатах каждого вектора.

this.graphics.fillPointShape(this.point1, 20);
this.graphics.fillPointShape(this.point2, 20);

Метод fillPointShape() принимает объект, реализующий интерфейс точки (как Vector2), и радиус.

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

Для запуска примера необходимо создать конфигурационный объект игры и инициализировать экземпляр Phaser.Game.

Конфигурация задает размеры окна (800x600), автоматический выбор рендерера (Phaser.AUTO), идентификатор HTML-контейнера и ссылку на наш класс сцены Example.

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

const game = new Phaser.Game(config);

После этого Phaser берет на себя управление циклом обновления и отрисовки, вызывая методы create() один раз при инициализации, а update() — на каждом кадре.

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

Метод floor() для векторов в Phaser — это простой, но мощный инструмент для контроля над координатами. Он незаменим, когда вам нужно «привязать» движение спрайта к сетке, обеспечить четкое пиксельное позиционирование или согласовать координаты с тайловой картой, где индексы ячеек являются целыми числами. **Идеи для экспериментов:** 1. Попробуйте применить floor() не только к Y, но и к X-координате, управляя движением по горизонтали. 2. Создайте спрайт и привяжите его позицию к округленному вектору, чтобы увидеть «ступенчатое» движение. 3. Используйте метод round() (округляет до ближайшего целого) или ceil() (округляет вверх) класса Vector2 и сравните визуальный результат с floor(). 4. Поэкспериментируйте с анимацией по диагонали, применяя округление к обоим компонентам вектора.