О чем этот пример
В разработке игр часто требуется направлять объекты перпендикулярно поверхности или рассчитывать отскоки. Вектор нормали — ключевой математический инструмент для таких задач. Phaser предоставляет простой способ его получения для любого отрезка через `Phaser.Geom.Line.GetNormal()`. Эта статья покажет, как использовать нормаль для создания динамических визуальных эффектов и симуляции физических взаимодействий, что полезно для реалистичного поведения снарядов, частиц или скольжения персонажей вдоль стен.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
normal;
line;
graphics;
create ()
{
this.graphics = this.add.graphics({ lineStyle: { width: 3, color: 0xaa00aa, alpha: 0.6 } });
this.line = new Phaser.Geom.Line(390, 300, 410, 300);
// if we omit a parameter, new Point instance will be created and returned
this.normal = Phaser.Geom.Line.GetNormal(this.line);
}
update ()
{
if (this.line.y2 > -150)
{
this.graphics.strokeLineShape(this.line);
// normal is a directly perpendicular vector to supplied line
// this moves the second line point 15px away in perpendicular direction
this.line.x2 += this.normal.x * 15;
this.line.y2 += this.normal.y * 15;
// we can also supply an instance of Point that will be modified
Phaser.Geom.Line.GetNormal(this.line, this.normal);
}
}
}
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое нормаль к линии?
Нормаль к линии — это единичный вектор (длиной 1), который направлен строго перпендикулярно этой линии. В 2D у линии есть две возможные нормали, направленные в противоположные стороны. Phaser.Geom.Line.GetNormal() возвращает одну из них (конкретное направление зависит от внутренней реализации).
Этот вектор незаменим для расчетов, где важно направление «от поверхности»: - Отражение объекта от линии (как мяч от стены). - Движение объекта вдоль линии (скольжение). - Определение, с какой стороны от линии находится точка.
Получение нормали: теория и код
Метод GetNormal — статический метод класса Phaser.Geom.Line. Он принимает линию и, опционально, объект Phaser.Geom.Point для результата.
// Создаем линию от точки (x1, y1) до точки (x2, y2)
this.line = new Phaser.Geom.Line(390, 300, 410, 300);
// Вариант 1: Получить новый объект Point с нормалью
this.normal = Phaser.Geom.Line.GetNormal(this.line);
// Вариант 2: Перезаписать существующий объект Point (экономит память)
let myPoint = new Phaser.Geom.Point();
Phaser.Geom.Line.GetNormal(this.line, myPoint);
// Теперь myPoint содержит (x, y) нормали
В первом варианте функция создает и возвращает новый экземпляр Point. Во втором — перезаписывает координаты переданного объекта myPoint, что эффективнее при частых вызовах в цикле update().
Практика: визуализация и движение по нормали
Давайте рассмотрим, как пример использует нормаль для анимации. В create() мы получаем начальную нормаль для горизонтальной линии. В update() мы постепенно удлиняем линию, смещая её конечную точку в направлении нормали.
update ()
{
if (this.line.y2 > -150)
{
this.graphics.strokeLineShape(this.line);
// Смещаем конечную точку (x2, y2) на 15 пикселей в направлении нормали
this.line.x2 += this.normal.x * 15;
this.line.y2 += this.normal.y * 15;
// Пересчитываем нормаль для НОВОГО положения линии
// и записываем результат в существующий this.normal
Phaser.Geom.Line.GetNormal(this.line, this.normal);
}
}
Ключевые моменты:
1. `this.normal.x * 15` и `this.normal.y * 15` — это проекции смещения на 15 пикселей по осям X и Y. Так как нормаль — единичный вектор, умножение на скаляр (15) задает точное расстояние смещения.
2. После изменения линии (`this.line`) нормаль пересчитывается. Если этого не сделать, движение продолжится по старому направлению, даже когда линия уже повернулась.
3. Цикл останавливается, когда `y2` достигает -150, предотвращая бесконечную анимацию.
Применение в играх: отскок и скольжение
Вот как можно применить нормаль в игровой логике.
**Простое отражение вектора скорости:**
// Допустим, ball.velocity — это объект Phaser.Math.Vector2
// line — это линия, от которой происходит отскок
let normal = Phaser.Geom.Line.GetNormal(line);
// Формула отражения: newVelocity = velocity - 2 * (velocity · normal) * normal
let dotProduct = ball.velocity.x * normal.x + ball.velocity.y * normal.y;
ball.velocity.x = ball.velocity.x - 2 * dotProduct * normal.x;
ball.velocity.y = ball.velocity.y - 2 * dotProduct * normal.y;
**Скольжение вдоль стены (обнуление скорости вдоль нормали):**
// Если объект коснулся стены, оставляем только ту компоненту скорости,
// которая перпендикулярна нормали (т.е. параллельна стене)
let normal = Phaser.Geom.Line.GetNormal(wallLine);
let dotProduct = sprite.body.velocity.x * normal.x + sprite.body.velocity.y * normal.y;
// Вычитаем из скорости компоненту, направленную вдоль нормали (в стену)
sprite.body.velocity.x -= dotProduct * normal.x;
sprite.body.velocity.y -= dotProduct * normal.y;
Эти приемы работают с любыми системами координат и углами поворота линий.
Что попробовать дальше
Метод Phaser.Geom.Line.GetNormal() — это мощный, но простой инструмент для работы с геометрией. Он открывает путь к реалистичной физике отскоков, управляемому движению вдоль поверхностей и точным пространственным расчетам. Для экспериментов попробуйте
- Создать лабиринт, где шарик отражается от его стен
- Заставить частицы системы «огонь» или «дым» двигаться перпендикулярно поверхности объекта
- Реализовать механику «прилипания» и скольжения персонажа по сложному рельефу, используя нормали сегментов платформы
