О чем этот пример
Работа с векторами — основа для множества игровых механик: от простого поворота объекта до сложного расчёта отскоков и траекторий. В этой статье мы разберём, как использовать метод `normalizeLeftHand()` (также известный как `perp()`) класса `Phaser.Math.Vector2` для быстрого получения перпендикулярного вектора. Этот приём незаменим при вычислении нормалей для столкновений, создании эффектов разлёта частиц или реализации управления, основанного на векторах.
Версия 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, -250);
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();
this.graphics.lineBetween(400, 300, 400 + this.point.x, 300 + this.point.y);
// rotates the point around 0/0 at 90 degrees towards right
// the result is a vector perpendicular to the original vector
this.point.normalizeLeftHand();
this.graphics.lineStyle(2, 0x00aa00);
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);
Что делает метод normalizeLeftHand?
Метод normalizeLeftHand() (или его синоним perp()) выполняет две операции одновременно. Во-первых, он нормализует вектор, то есть приводит его к единичной длине, сохраняя направление. Во-вторых, и это главное, он поворачивает этот единичный вектор на 90 градусов против часовой стрелки (в левую сторону).
В результате из исходного вектора мы мгновенно получаем вектор, перпендикулярный ему, что является частой и важной операцией в физике 2D-игр.
Давайте посмотрим на его работу на простом примере. Исходный вектор (3, 0) после применения метода превратится в (0, -1).
let vec = new Phaser.Math.Vector2(3, 0);
vec.normalizeLeftHand(); // Теперь vec равен (0, -1)
Разбираем пример: интерактивный перпендикуляр
Рассмотренный пример создаёт интерактивную визуализацию. Пользователь двигает курсор, а программа в реальном времени рисует исходный вектор и перпендикулярный к нему.
В методе create() инициализируются графика, начальная точка-вектор и обработчик события движения мыши.
create ()
{
// Создаём объект Graphics для рисования линий
this.graphics = this.add.graphics({ lineStyle: { width: 3, color: 0x2266aa } });
// Создаём вектор, который будет меняться от позиции курсора
this.point = new Phaser.Math.Vector2(250, -250);
// При движении мыши обновляем координаты вектора
this.input.on('pointermove', pointer =>
{
// Координаты считаются относительно центра экрана (400, 300)
this.point.x = pointer.x - 400;
this.point.y = pointer.y - 300;
this.redraw(); // Перерисовываем сцену
});
this.redraw(); // Первоначальная отрисовка
}
Ключевой момент — координаты вектора this.point хранятся не в абсолютных координатах экрана, а относительно центра (400, 300). Это упрощает математические операции, так как вектор начинается в условной точке (0, 0).
Логика отрисовки в методе redraw
Метод redraw() очищает холст и рисует две линии: исходный вектор и перпендикулярный к нему.
redraw ()
{
// Очищаем предыдущие рисунки
this.graphics.clear();
// Рисуем ИСХОДНЫЙ вектор (синяя линия) от центра к позиции курсора
this.graphics.lineBetween(400, 300, 400 + this.point.x, 300 + this.point.y);
// Меняем вектор this.point! Теперь он станет перпендикулярным единичным вектором.
this.point.normalizeLeftHand();
// Меняем стиль линии на зелёный и рисуем ПЕРПЕНДИКУЛЯРНЫЙ вектор
this.graphics.lineStyle(2, 0x00aa00);
this.graphics.lineBetween(400, 300, 400 + this.point.x, 300 + this.point.y);
}
Важно понимать последовательность: сначала мы используем исходные координаты this.point для рисования синей линии. Затем применяем this.point.normalizeLeftHand(), что **изменяет сам вектор** this.point. После этого вызова this.point уже не указывает на курсор, а является перпендикулярным единичным вектором. Именно его мы и рисуем зелёной линией из того же центра.
Практическое применение в играх
Перпендикулярный вектор (нормаль) находит множество применений.
1. **Физика столкновений:** При столкновении шара со стеной вектор отскока рассчитывается через нормаль к поверхности. normalizeLeftHand() помогает быстро её найти.
// Допустим, вектор стены направлен вдоль оси X (1, 0)
let wallDirection = new Phaser.Math.Vector2(1, 0);
// Нормаль к стене (направленная вверх)
let wallNormal = wallDirection.clone().normalizeLeftHand(); // (0, -1)
2. **Движение "боком":** Если у игрока есть вектор взгляда forward, то вектор движения вправо/влево — это его перпендикуляр.
let forward = new Phaser.Math.Vector2(0, -1); // Смотрит вверх
let right = forward.clone().normalizeLeftHand(); // Смотрит вправо (1, 0)
3. **Разлёт частиц:** При взрыве частицы разлетаются во все стороны. Перпендикуляр к вектору от центра взрыва даёт одно из возможных направлений.
normalizeLeftHand vs perp vs normalizeRightHand
В Phaser 3 для одной и той же операции есть несколько имён, что может сбить с толку.
* normalizeLeftHand() и perp() — это **один и тот же метод**. Он поворачивает вектор на 90° **против** часовой стрелки.
* normalizeRightHand() (или rperp()) делает обратное: нормализует и поворачивает на 90° **по** часовой стрелке.
Выбор метода зависит от нужного вам направления поворота в вашей системе координат. В 2D Phaser ось Y направлена **вниз**, поэтому «левый» поворот (против часовой стрелки) визуально может выглядеть как поворот вправо, если вектор указывает вниз. Всегда проверяйте направление в своём конкретном случае.
let v = new Phaser.Math.Vector2(0, 1); // Вектор, смотрящий вниз
v.normalizeLeftHand(); // Результат: (1, 0) (поворот против ЧЧ -> вправо)
v.normalizeRightHand(); // Результат: (-1, 0) (поворот по ЧЧ -> влево)
Что попробовать дальше
Метод normalizeLeftHand() (он же perp()) — это мощный и лаконичный инструмент для работы с векторами в Phaser 3. Он позволяет в одну строку кода получить перпендикулярный единичный вектор, что критически важно для расчётов в физике, управлении и визуальных эффектах.
**Идеи для экспериментов:**
1. Создайте шар, который отскакивает от наклонной платформы, используя перпендикуляр к её вектору как нормаль.
2. Реализуйте космический корабль, который может двигаться не только вперёд-назад, но и строго вбок (стрифинг), используя перпендикуляр к вектору направления.
3. Визуализируйте поле нормалей для сложной фигуры, рисуя маленькие перпендикулярные линии вдоль её контура.
