О чем этот пример
При создании игр часто возникает задача перемещения объектов по нелинейным траекториям, например, по орбите планеты или по контуру сложной фигуры. Обычный расчет по окружности здесь не подойдет. В этом примере мы покажем, как использовать метод `Phaser.Geom.Ellipse.CircumferencePoint` для получения точных координат на эллипсе по заданному углу. Это полезно для создания плавных орбитальных движений, прицеливания по дуге или визуализации геометрических взаимодействий в реальном времени.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
angle;
create ()
{
const graphics = this.add.graphics({ fillStyle: { color: 0x2266aa } });
const ellipse = new Phaser.Geom.Ellipse(400, 300, 250, 150);
let circumferencePoint = new Phaser.Math.Vector2(275, 300);
const centerPoint = new Phaser.Math.Vector2(400, 300);
const line = new Phaser.Geom.Line(400, 300, 275, 300);
const text1 = this.add.text(20, 50, 'Circumference Point:');
const text2 = this.add.text(20, 75, 'Angle:');
this.input.on('pointermove', pointer =>
{
this.angle = Phaser.Math.Angle.Between(400, 300, pointer.x, pointer.y);
circumferencePoint = Phaser.Geom.Ellipse.CircumferencePoint(ellipse, this.angle);
line.x2 = circumferencePoint.x;
line.y2 = circumferencePoint.y;
text1.setText(`Circumference Point: (${circumferencePoint.x}, ${circumferencePoint.y})`);
text2.setText(`Angle: ${this.angle}`);
graphics.fillPointShape(circumferencePoint, 20);
draw();
});
draw();
function draw ()
{
graphics.clear();
graphics.lineStyle(2, 0x00aaaa);
graphics.strokeEllipseShape(ellipse);
graphics.strokeLineShape(line);
graphics.fillPointShape(centerPoint, 10);
graphics.fillPointShape(circumferencePoint, 20);
}
}
}
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и инициализация объектов
В начале создается графика для отрисовки, определяется эллипс и несколько ключевых точек. Векторы используются для удобной работы с координатами.
const graphics = this.add.graphics({ fillStyle: { color: 0x2266aa } });
const ellipse = new Phaser.Geom.Ellipse(400, 300, 250, 150);
let circumferencePoint = new Phaser.Math.Vector2(275, 300);
const centerPoint = new Phaser.Math.Vector2(400, 300);
const line = new Phaser.Geom.Line(400, 300, 275, 300);
const text1 = this.add.text(20, 50, 'Circumference Point:');
const text2 = this.add.text(20, 75, 'Angle:');
Эллипс задается центром (400, 300) и радиусами (250 по X, 150 по Y). Линия и начальная точка circumferencePoint визуализируют связь между центром и точкой на контуре. Текстовые объекты будут выводить актуальные данные.
Обработка движения указателя и расчет точки
Сердце примера — обработчик события pointermove. При каждом движении мыши вычисляется угол между центром эллипса и курсором, а затем находится соответствующая точка на контуре фигуры.
this.input.on('pointermove', pointer => {
this.angle = Phaser.Math.Angle.Between(400, 300, pointer.x, pointer.y);
circumferencePoint = Phaser.Geom.Ellipse.CircumferencePoint(ellipse, this.angle);
line.x2 = circumferencePoint.x;
line.y2 = circumferencePoint.y;
text1.setText(`Circumference Point: (${circumferencePoint.x}, ${circumferencePoint.y})`);
text2.setText(`Angle: ${this.angle}`);
graphics.fillPointShape(circumferencePoint, 20);
draw();
});
Phaser.Math.Angle.Between возвращает угол в радианах. Этот угол передается в Phaser.Geom.Ellipse.CircumferencePoint, который и выполняет главную работу: рассчитывает координаты на эллипсе, учитывая его растяжение по осям. После этого обновляется линия, текст и вызывается функция перерисовки.
Функция отрисовки графики
Функция draw отвечает за визуализацию всех элементов на холсте. Важно очищать графику перед каждой отрисовкой, чтобы избежать наложения.
function draw ()
{
graphics.clear();
graphics.lineStyle(2, 0x00aaaa);
graphics.strokeEllipseShape(ellipse);
graphics.strokeLineShape(line);
graphics.fillPointShape(centerPoint, 10);
graphics.fillPointShape(circumferencePoint, 20);
}
Сначала graphics.clear() удаляет все, что было нарисовано ранее. Затем задается стиль линии, и отрисовываются контур эллипса, соединительная линия, а также две точки: центр (меньшего размера) и текущая точка на контуре (большего размера).
Практическое применение в играх
Этот метод выходит за рамки простой демонстрации. Его можно использовать для:
* **Орбитального движения:** Поместите спрайт в точку circumferencePoint и изменяйте угол в update для плавного вращения по эллипсу.
* **Траектории снаряда:** Рассчитайте несколько точек по дуге для отрисовки предсказания полета.
* **Генерации патрульных путей:** Задайте эллипс как зону патрулирования для NPC.
// Пример: перемещение спрайта по орбите
function update(time, delta) {
this.angle += 0.01; // Медленно увеличиваем угол
const orbitPoint = Phaser.Geom.Ellipse.CircumferencePoint(ellipse, this.angle);
this.playerSprite.setPosition(orbitPoint.x, orbitPoint.y);
}
Ключевое отличие от работы с окружностью — учет разных радиусов эллипса, что делает движение визуально более интересным и естественным для вытянутых форм.
Что попробовать дальше
Метод CircumferencePoint — это мощный и точный инструмент для привязки объектов и вычислений к контуру эллипса. Он избавляет разработчика от необходимости реализации сложной тригонометрической формулы. Для экспериментов попробуйте анимировать радиусы эллипса во время движения точки, создать несколько объектов, движущихся по одной орбите с разной скоростью, или использовать эту технику для определения точки столкновения снаряда с elliptical hitbox.
