О чем этот пример
В игровой разработке столкновения и отскоки — фундаментальные механики. Чтобы реалистично рассчитать направление отскока мяча от стены или траекторию скольжения вдоль препятствия, вам понадобится нормаль к поверхности. В этой статье мы разберем, как получить и визуализировать вектор нормали для линии в Phaser — ключевой шаг к созданию физически правдоподобных взаимодействий. На примере из официальной документации мы наглядно посмотрим, как API Phaser.Geom.Line позволяет вычислить компоненты нормали, и как эти значения меняются в реальном времени при изменении угла линии. Это знание станет основой для ваших собственных систем столкновений, управления снарядами или создания скользящего движения вдоль геометрических форм.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
create ()
{
const graphics = this.add.graphics({ lineStyle: { width: 4, color: 0xaa00aa } });
const line = new Phaser.Geom.Line(400, 300, 550, 300);
const textX = this.add.text(50, 50, '');
const textY = this.add.text(50, 75, '');
this.input.on('pointermove', pointer =>
{
line.x2 = pointer.x;
line.y2 = pointer.y;
redraw();
});
redraw();
function redraw ()
{
graphics.clear();
graphics.strokeLineShape(line);
const normalX = Phaser.Geom.Line.NormalX(line);
const normalY = Phaser.Geom.Line.NormalY(line);
graphics.lineStyle(2, 0xaa0000);
graphics.lineBetween(400, 300, 400 + normalX * 100, 300);
graphics.lineStyle(2, 0x00aa00);
graphics.lineBetween(400, 300, 400, 300 + normalY * 100);
textX.setText(`Line Normal X: ${normalX}`);
textY.setText(`Line Normal Y: ${normalY}`);
}
}
}
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое нормаль линии?
Нормаль линии — это единичный вектор (длиной 1), перпендикулярный этой линии. Он всегда направлен вправо от начальной точки линии к ее конечной точке при стандартном математическом представлении.
Вектор нормали состоит из двух компонентов: NormalX и NormalY. Эти значения лежат в диапазоне от -1 до 1 и описывают направление. Например, нормаль (1, 0) указывает строго вправо, (0, 1) — строго вниз, а (-0.707, 0.707) — на 45 градусов вверх-влево.
В физике игр именно этот вектор используется для расчета отскока: чтобы объект отскочил от стены, его скорость отражается относительно нормали к поверхности в точке столкновения.
Разбор примера: создание сцены и линии
В примере создается базовая сцена и линия, второй конец которой привязан к курсору мыши. Это позволяет интерактивно менять угол и наблюдать за изменением нормали.
class Example extends Phaser.Scene
{
create ()
{
const graphics = this.add.graphics({ lineStyle: { width: 4, color: 0xaa00aa } });
const line = new Phaser.Geom.Line(400, 300, 550, 300);
Здесь создается объект Graphics для отрисовки и линия с началом в точке (400, 300) и начальным концом в (550, 300). Линия будет отрисована фиолетовым цветом (0xaa00aa). Создаются также два текстовых объекта для вывода числовых значений нормали.
this.input.on('pointermove', pointer =>
{
line.x2 = pointer.x;
line.y2 = pointer.y;
redraw();
});
Обработчик события pointermove обновляет координаты второй точки линии (x2, y2) текущей позицией курсора и вызывает функцию redraw() для обновления изображения на экране.
Вычисление и отрисовка нормали
Ключевая логика происходит в функции redraw. Она очищает холст, рисует обновленную линию, вычисляет ее нормаль и визуализирует компоненты нормали в виде отдельных векторов.
function redraw ()
{
graphics.clear();
graphics.strokeLineShape(line);
const normalX = Phaser.Geom.Line.NormalX(line);
const normalY = Phaser.Geom.Line.NormalY(line);
С помощью статических методов Phaser.Geom.Line.NormalX(line) и Phaser.Geom.Line.NormalY(line) вычисляются компоненты вектора нормали для текущей линии. Эти методы возвращают числа с плавающей запятой.
graphics.lineStyle(2, 0xaa0000);
graphics.lineBetween(400, 300, 400 + normalX * 100, 300);
graphics.lineStyle(2, 0x00aa00);
graphics.lineBetween(400, 300, 400, 300 + normalY * 100);
Здесь происходит визуализация. Красная линия (0xaa0000) отображает X-компонент нормали. Она рисуется горизонтально от начальной точки линии. Координата X конечной точки рассчитывается как 400 + normalX * 100. Умножение на 100 — это масштабирование, чтобы вектор нормали (длиной 1) было хорошо видно на экране. Аналогично зеленая линия (0x00aa00) отображает Y-компонент вертикально. Это разложение единичного вектора нормали по осям.
textX.setText(`Line Normal X: ${normalX}`);
textY.setText(`Line Normal Y: ${normalY}`);
}
}
}
Конфигурация и запуск игры
Пример завершается стандартной для Phaser конфигурацией игры, которая создает экземпляр сцены, описанной выше.
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Эта конфигурация создает холст размером 800x600 пикселей и инициализирует нашу сцену Example.
Что попробовать дальше
Вычисление нормали линии — простая, но мощная операция, открывающая путь к созданию сложной игровой физики. Используя Phaser.Geom.Line.NormalX и NormalY, вы можете реализовать реалистичный отскок мяча от наклонной поверхности или рассчитать силу трения, действующую вдоль препятствия.
**Идеи для экспериментов:**
1. Создайте спрайт-шарик и заставьте его отскакивать от нарисованной линии, используя нормаль для расчета нового вектора скорости.
2. Реализуйте "скольжение" объекта вдоль сложного контура: когда объект сталкивается с линией, обнулите компонент скорости, направленный вдоль нормали, оставив только касательную составляющую.
3. Используйте нормаль для визуальных эффектов, например, для расчета направления и длины блика на глянцевой поверхности, представленной линией.
