О чем этот пример
Векторная математика — основа многих игровых механик, от управления персонажем до расчёта траекторий. Метод `.project()` класса `Phaser.Math.Vector2` позволяет спроецировать один вектор на другой, что полезно для создания теней, отражений, определения ближайшей точки на линии или расчёта силы, действующей в определённом направлении. Эта статья наглядно показывает, как работает проекция векторов в реальном времени, реагируя на движение курсора.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
angle = 0;
projectedPoint;
point2;
point;
graphics;
create ()
{
this.graphics = this.add.graphics({ lineStyle: { width: 2, color: 0x2266aa }, fillStyle: { color: 0xaa0000 } });
this.point = new Phaser.Math.Vector2(250, 0);
this.point2 = new Phaser.Math.Vector2(250, 0);
this.projectedPoint = this.point2.clone().project(this.point);
this.input.on('pointermove', pointer =>
{
this.point2.copy(pointer);
this.point2.x -= 400;
this.point2.y -= 300;
});
}
update ()
{
this.graphics.clear();
this.angle += 0.005;
// vector starting at 0/0
this.point.setTo(Math.cos(this.angle) * 250, Math.sin(this.angle) * 250);
// drawn from the center (as if center was 0/0)
this.graphics.lineBetween(400, 300, 400 + this.point.x, 300 + this.point.y);
this.graphics.lineStyle(2, 0x00aa00);
this.graphics.lineBetween(400, 300, 400 + this.point2.x, 300 + this.point2.y);
this.projectedPoint = this.point2.clone().project(this.point);
// move relative to center
this.projectedPoint.x += 400;
this.projectedPoint.y += 300;
this.graphics.fillPointShape(this.projectedPoint, 15);
this.graphics.lineStyle(1, 0xaa0000);
this.graphics.lineBetween(this.point2.x + 400, this.point2.y + 300, this.projectedPoint.x, this.projectedPoint.y);
}
}
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Суть проекции вектора
Проекция вектора `aна векторb— это вектор, показывающий, насколькоa"ложится" на направлениеb. Его можно представить как тень или отражение одного вектора на другой. В Phaser для этого используется метод.project()`.
let projectedVector = vectorA.clone().project(vectorB);
Метод модифицирует вектор, на котором вызывается, превращая его в проекцию вектора-аргумента. Поэтому обычно используют клон исходного вектора, чтобы не потерять исходные данные. Визуально это точка на линии вектора `b, ближайшая к концу вектораa`.
Настройка сцены и векторов
В примере создаётся вращающийся вектор (this.point) и вектор, следующий за курсором (this.point2). Оба вектора заданы относительно центра экрана (координаты [400, 300]), что упрощает расчёты.
create() {
this.graphics = this.add.graphics({ lineStyle: { width: 2, color: 0x2266aa }, fillStyle: { color: 0xaa0000 } });
this.point = new Phaser.Math.Vector2(250, 0);
this.point2 = new Phaser.Math.Vector2(250, 0);
this.projectedPoint = this.point2.clone().project(this.point);
}
Графический объект this.graphics будет рисовать линии и точки. Обработчик события pointermove обновляет this.point2, переводя координаты курсора в систему отсчёта с центром в (400, 300).
Алгоритм обновления и визуализация
В методе update() происходит анимация и перерисовка. Вращающийся вектор this.point задаётся через синус и косинус.
this.angle += 0.005;
this.point.setTo(Math.cos(this.angle) * 250, Math.sin(this.angle) * 250);
Затем рисуются оба вектора, выходящие из центра экрана. Ключевой момент — вычисление проекции вектора курсора на вращающийся вектор.
this.projectedPoint = this.point2.clone().project(this.point);
this.projectedPoint.x += 400;
this.projectedPoint.y += 300;
Поскольку this.point2 и this.point заданы относительно центра, результат проекции также получается в этой системе. Перед отрисовкой его координаты возвращаются в глобальные, прибавляя смещение центра (400, 300).
Интерпретация результата
Спроецированная точка отображается красным кругом. Зелёная линия — это вектор от курсора (this.point2) до его проекции на синий вектор. Эта линия всегда перпендикулярна синему вектору, что и является геометрическим свойством проекции: кратчайшее расстояние от точки до линии.
this.graphics.fillPointShape(this.projectedPoint, 15);
this.graphics.lineStyle(1, 0xaa0000);
this.graphics.lineBetween(this.point2.x + 400, this.point2.y + 300, this.projectedPoint.x, this.projectedPoint.y);
Таким образом, визуализация помогает понять, что проекция — это точка на синей линии, куда "падает" перпендикуляр из точки курсора.
Что попробовать дальше
Метод .project() — мощный инструмент для работы с векторами, который можно использовать для расчёта отражения снаряда от поверхности, определения силы, толкающей объект вдоль дороги, или нахождения ближайшей к игроку точки на заданном пути. Попробуйте изменить пример: спроецируйте не вектор курсора, а, например, скорость одного объекта на направление к другому, чтобы смоделировать погоню, или используйте проекцию для создания простых теней от объектов на наклонных плоскостях.
