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

Векторы — основа игровой физики, анимации и логики. Умение работать с ними напрямую влияет на реалистичность поведения объектов. В этом примере мы не просто разделим вектор на скаляр, а создадим интерактивную визуализацию, которая наглядно покажет, как последовательное деление координат вектора создаёт геометрические паттерны. Этот подход поможет глубже понять, как математические операции трансформируют игровое пространство.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        this.graphics = this.add.graphics({ fillStyle: { color: 0x2266aa } });

        this.input.on('pointermove', pointer =>
        {
            this.redraw(1.01 + pointer.x / 800, 1.01 + pointer.y / 600);
        });

        this.redraw(1.2, 1.2);
    }

    redraw (divX, divY)
    {
        this.graphics.clear();

        const point = new Phaser.Math.Vector2(800, 600);

        while (point.x > 10 || point.y > 10)
        {
            this.graphics.fillPointShape(point, 20);

            point.x /= divX;
            point.y /= divY;
        }
    }
}

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

const game = new Phaser.Game(config);

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

В методе create() инициализируется сцена. Создаётся объект Graphics для отрисовки примитивов — в нашем случае, точек. Затем на событие перемещения указателя (pointermove) вешается обработчик. Координаты курсора (pointer.x, pointer.y) нормализуются, чтобы получить удобные для расчёта делители в диапазоне примерно от 1 до 2. Эти делители передаются в основную функцию перерисовки redraw().

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

    this.input.on('pointermove', pointer =>
    {
        this.redraw(1.01 + pointer.x / 800, 1.01 + pointer.y / 600);
    });

    this.redraw(1.2, 1.2);
}

Сердце примера: функция redraw и деление вектора

Функция redraw(divX, divY) отвечает за всю визуализацию. В её начале холст очищается вызовом this.graphics.clear(). Затем создаётся исходный вектор point с координатами (800, 600) — это правая нижняя часть экрана в нашем примере.

Ключевой процесс происходит в цикле while. Пока хотя бы одна из координат вектора больше 10, выполняется два действия: 1. Текущее положение вектора отрисовывается на холсте как закрашенная точка. 2. Координаты вектора point делятся на переданные делители divX и divY.

Операция point.x /= divX — это сокращённая запись point.x = point.x / divX. Таким образом, на каждой итерации вектор "сдвигается" к началу координат (0, 0), создавая последовательность точек.

redraw (divX, divY)
{
    this.graphics.clear();

    const point = new Phaser.Math.Vector2(800, 600);

    while (point.x > 10 || point.y > 10)
    {
        this.graphics.fillPointShape(point, 20);

        point.x /= divX;
        point.y /= divY;
    }
}

Как работает цикл и формируется узор

Цикл while (point.x > 10 || point.y > 10) гарантирует, что отрисовка остановится, когда вектор приблизится к началу координат (условие выхода — обе координаты меньше или равны 10). Начальная точка — (800, 600). После первого деления координаты становятся, например, (800/1.5, 600/1.2). Процесс повторяется, и каждая новая точка ложится ближе к (0,0).

Разные делители для X и Y (divX и divY) приводят к тому, что координаты уменьшаются с разной скоростью. Если divX больше divY, то X-координата будет сокращаться быстрее, и траектория точек сильнее "завалится" по вертикали. Именно это позволяет управлять формой паттерна движением мыши. Без условия > 10 цикл стал бы бесконечным, так как деление никогда не даст точного нуля.

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

Стандартный конфигурационный объект Phaser config задаёт размеры холста, автоматический выбор рендерера (Phaser.AUTO), элемент-контейнер и нашу сцену Example. Инстанс игры new Phaser.Game(config) запускает весь процесс.

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

const game = new Phaser.Game(config);

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

Пример превращает абстрактную операцию деления вектора в наглядный интерактивный инструмент. Вы увидели, как с помощью Phaser.Math.Vector2 и Graphics можно создавать динамические визуализации для отладки или обучения. Для экспериментов попробуйте: изменить начальную точку вектора; заменить деление на умножение или сложение; использовать один общий делитель для обеих координат; или рисовать не точки, а линии между итерациями, чтобы увидеть траекторию.