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

Работа с векторами — фундамент игровой разработки, особенно когда речь идёт о движении, направлении и физике. В этом примере мы разберём метод `normalize()` класса `Phaser.Math.Vector2`. Этот метод превращает любой вектор в единичный — вектор с длиной (магнитудой), равной 1, но сохраняющий исходное направление. Зачем это нужно? Единичные векторы — это универсальные указатели направления. Умножив их на нужную скорость или силу, вы получите предсказуемое и контролируемое движение объекта, независимо от начальной длины вектора. В примере мы визуализируем, как нормализованный вектор всегда даёт линию фиксированной длины.

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

Живой запуск

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

Исходный код


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

        this.point = new Phaser.Math.Vector2(250, 0);

        this.text = this.add.text(50, 50, '');

        this.input.on('pointermove', pointer =>
        {
            //  Set relative to center
            this.point.x = pointer.x - 400;
            this.point.y = pointer.y - 300;

            this.redraw();
        });

        this.redraw();
    }

    redraw ()
    {
        this.graphics.clear();

        // normalized point will always have a magnitude of 1
        this.point.normalize();

        this.text.setText('Normalized point: ' + this.point.x + '/' + this.point.y);

        // we can multiply it with desired length to get desired magnitude
        this.point.x *= 200;
        this.point.y *= 200;

        // this will always draw a line that's 200 px long
        this.graphics.lineBetween(400, 300, 400 + this.point.x, 300 + this.point.y);
    }
}

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

const game = new Phaser.Game(config);

Суть нормализации: от хаоса к порядку

Представьте, что вектор — это стрелка от центра экрана к положению курсора. Её длина зависит от того, насколько далеко вы отвели мышь. Для расчётов движения такая изменчивая длина неудобна.

Метод normalize() решает эту проблему. Он берёт текущий вектор и преобразует его так, чтобы направление осталось прежним, а длина стала равна ровно 1. Это делается путём деления каждой компоненты (x и y) на исходную длину вектора.

this.point.normalize();

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

Создание сцены и отслеживание курсора

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

Событие pointermove обновляет координаты вектора, делая их относительными к центру экрана (точке [400, 300]). Это стандартный приём, чтобы рассматривать вектор как направление из центра.

this.point.x = pointer.x - 400;
this.point.y = pointer.y - 300;

После каждого движения мыши вызывается метод redraw() для обновления визуализации.

Визуализация: от единичного вектора к фиксированной линии

Вся магия происходит в методе redraw(). Сначала мы нормализуем вектор, получая чистый указатель направления.

this.point.normalize();

Затем, чтобы увидеть результат на экране, мы масштабируем этот единичный вектор, умножая его компоненты на желаемую длину линии (200 пикселей). Ключевой момент: каким бы ни было исходное положение мыши, после нормализации умножение на 200 даст вектор длиной ровно 200.

this.point.x *= 200;
this.point.y *= 200;

Наконец, с помощью graphics.lineBetween рисуется линия из центра экрана в новую, рассчитанную точку. Длина этой линии всегда будет равна 200 пикселям, что наглядно демонстрирует мощь нормализации для контроля над величиной.

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

Где это использовать? Практически везде, где есть движение.

1. **Задание скорости игрока:** Получите вектор направления от джойстика, нормализуйте его и умножьте на значение скорости. Персонаж будет двигаться с постоянной скоростью в любом направлении.

// directionVector — вектор от джойстика
    directionVector.normalize();
    player.body.setVelocity(directionVector.x * speed, directionVector.y * speed);

2. **Расчёт отдачи или силы:** При столкновении можно нормализовать вектор, указывающий от точки удара, и умножить его на силу отдачи.

3. **Направление взгляда или выстрела:** Единичный вектор идеально подходит для задания rotation спрайта или направления пули через setVelocity.

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

Нормализация вектора — это простой, но мощный инструмент для отделения направления от величины. Он делает код, отвечающий за движение, чистым, предсказуемым и лишённым неожиданных скачков скорости. Поэкспериментируйте: измените множитель 200 в коде на переменную, управляемую клавишами, чтобы динамически менять длину линии. Или создайте несколько спрайтов, которые будут следовать за курсором, но с разными скоростями, используя один и тот же нормализованный вектор направления, умноженный на разные значения.