О чем этот пример
Работа с геометрией — основа многих визуальных эффектов в играх. В этом примере мы разберем, как использовать метод `negate()` объекта `Phaser.Math.Vector2` для создания динамической, симметричной фигуры. Понимание этого принципа полезно для генерации сложных паттернов, траекторий снарядов или симметричных анимаций без дублирования кода.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
points;
step = 0.5;
graphics;
create ()
{
this.graphics = this.add.graphics({ lineStyle: { width: 1, color: 0x2266aa } });
this.points = [];
}
update ()
{
if (this.step <= Math.PI / 50)
{
return;
}
else
{
this.step = Math.max(this.step * 0.998, Math.PI / 50);
}
this.graphics.clear();
let i = 0;
for (let angle = 0; angle < Math.PI * 2; angle += this.step)
{
const point = this.points[i] || new Phaser.Math.Vector2();
point.setTo(Math.cos(angle) * 290, Math.sin(angle) * 290);
this.points[i] = point;
++i;
this.points[i] = point.clone().negate();
++i;
}
for (let j = 0; j < this.points.length; j++)
{
this.points[j].x += 400;
this.points[j].y += 300;
}
this.graphics.strokePoints(this.points, false);
}
}
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и данных
В начале создается базовый каркас сцены. Инициализируются массивы для хранения точек и объект Graphics для их отрисовки.
class Example extends Phaser.Scene
{
points;
step = 0.5;
graphics;
create ()
{
this.graphics = this.add.graphics({ lineStyle: { width: 1, color: 0x2266aa } });
this.points = [];
}
Ключевые объекты:
- points: пустой массив для хранения объектов Vector2.
- step: начальный угловой шаг для расчета позиций точек на окружности.
- graphics: контекст отрисовки с заданным стилем линии.
Динамическое обновление и расчет точек
В методе update() происходит основная логика. Сначала уменьшается угловой шаг step, что делает анимацию плавно замедляющейся.
if (this.step <= Math.PI / 50)
{
return;
}
else
{
this.step = Math.max(this.step * 0.998, Math.PI / 50);
}
Затем холст очищается, и начинается цикл по углам от 0 до 2π. Для каждого угла вычисляется точка на окружности радиусом 290 пикселей.
for (let angle = 0; angle < Math.PI * 2; angle += this.step)
{
const point = this.points[i] || new Phaser.Math.Vector2();
point.setTo(Math.cos(angle) * 290, Math.sin(angle) * 290);
this.points[i] = point;
Здесь point.setTo(x, y) задает координаты вектора. Важный момент: если в массиве points уже есть вектор по индексу `i`, он перезаписывается, что экономит память.
Создание симметрии с помощью negate()
Следующая строка — сердце примера. После сохранения исходной точки, индекс увеличивается, и в массив добавляется её зеркальная копия.
++i;
this.points[i] = point.clone().negate();
Метод clone() создает новый объект Vector2 с теми же координатами, что и point. Затем метод negate() инвертирует знак обеих компонент этого нового вектора (x = -x, y = -y). Таким образом, для каждой точки на окружности мы мгновенно получаем её противоположность относительно центра (0, 0), создавая симметричную фигуру.
Смещение и отрисовка фигуры
После генерации всех пар точек, их необходимо сместить в центр холста игры, так как изначально они рассчитываются относительно координат (0, 0).
for (let j = 0; j < this.points.length; j++)
{
this.points[j].x += 400;
this.points[j].y += 300;
}
Значения 400 и 300 — это смещение, примерно соответствующее центру холста размером 800x600. Наконец, все точки соединяются линией.
this.graphics.strokePoints(this.points, false);
Метод strokePoints() объекта Graphics принимает массив точек и флаг, указывающий, нужно ли замыкать фигуру (в нашем случае — нет).
Что попробовать дальше
Метод negate() — это мощный и лаконичный способ работы с векторной симметрией. Он избавляет от необходимости вручную вычислять противоположные координаты. Для экспериментов попробуйте изменить радиус окружности, поиграть со скоростью уменьшения шага step или применить negate() не ко всем, а к каждому второму вектору для создания более сложных асимметричных паттернов. Также можно поэкспериментировать с цветом или толщиной линии в зависимости от индекса точки.
