О чем этот пример
Веревки (Rope) в Phaser — это гибкие игровые объекты, которые могут имитировать физику канатов, проводов или даже хвостов. Они состоят из сегментов (текстур), натянутых между контрольными точками. Эта статья покажет, как создать интерактивный инструмент для рисования веревки мышью и затем анимировать ее точки, создавая эффект волны. Этот подход полезен для создания динамических элементов окружения, подвижных мостов или даже причесок персонажей.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
this.ropes = [];
this.count = 0;
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('pipe1', 'assets/rope/pipe1.png');
this.load.image('pipe2', 'assets/rope/pipe2.png');
this.load.image('pipe3', 'assets/rope/pipe3.png');
this.load.image('pipe4', 'assets/rope/phaser3-logo.png');
}
create ()
{
const debug = this.add.graphics().fillStyle(0xffffff);
const distance = 10;
const prev = new Phaser.Math.Vector2();
let pipe = 1;
let points = [];
this.input.on('pointerdown', (pointer) => {
points = [];
prev.x = pointer.x;
prev.y = pointer.y;
points.push(new Phaser.Math.Vector2(pointer.x, pointer.y));
debug.fillRect(pointer.x, pointer.y, 2, 2);
});
this.input.on('pointermove', (pointer) => {
if (pointer.isDown)
{
const x = pointer.x;
const y = pointer.y;
if (Phaser.Math.Distance.Between(x, y, prev.x, prev.y) > distance)
{
prev.x = x;
prev.y = y;
points.push(new Phaser.Math.Vector2(pointer.x, pointer.y));
debug.fillRect(pointer.x, pointer.y, 2, 2);
}
}
});
this.input.on('pointerup', (pointer) => {
debug.clear();
const rope = this.add.rope(0, 0, 'pipe' + pipe, null, points);
this.ropes.push(rope);
pipe++;
if (pipe === 5)
{
pipe = 1;
}
});
this.add.text(10, 10, 'Draw with the mouse', { font: '16px Courier', fill: '#ffffff' }).setShadow(1, 1).setDepth(1);
}
update ()
{
this.count += 0.1;
this.ropes.forEach((rope) => {
let points = rope.points;
for (let i = 0; i < points.length; i++)
{
if (rope.horizontal)
{
points[i].y += Math.sin(i * 0.15 + this.count) * 2;
}
else
{
points[i].x += Math.sin(i * 0.25 + this.count) * 2;
}
}
rope.setDirty();
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
let game = new Phaser.Game(config);
Загрузка текстур и подготовка сцены
В методе preload мы загружаем четыре различные текстуры для сегментов веревки. Они будут циклически использоваться при создании новых объектов. В конструкторе класса инициализируются массивы для хранения созданных веревок и переменная-счетчик для анимации.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('pipe1', 'assets/rope/pipe1.png');
this.load.image('pipe2', 'assets/rope/pipe2.png');
this.load.image('pipe3', 'assets/rope/pipe3.png');
this.load.image('pipe4', 'assets/rope/phaser3-logo.png');
}
Рисование траектории мышью
В методе create мы настраиваем интерактивность. Создается графический объект debug для визуализации процесса рисования. Основная логика разделена между тремя обработчиками событий указателя (мыши или касания).
При нажатии (pointerdown) начинается новый путь: очищается массив точек, запоминается начальная позиция, и первая точка добавляется в массив.
При движении (pointermove) проверяется, нажата ли кнопка. Новые точки добавляются в массив только если расстояние от предыдущей точки превышает заданный порог (distance). Это оптимизация, которая предотвращает создание избыточного количества точек при медленном движении, экономя ресурсы.
if (Phaser.Math.Distance.Between(x, y, prev.x, prev.y) > distance)
{
prev.x = x;
prev.y = y;
points.push(new Phaser.Math.Vector2(pointer.x, pointer.y));
debug.fillRect(pointer.x, pointer.y, 2, 2);
}
Создание игрового объекта Rope
Когда кнопка мыши отпущена (pointerup), графический объект debug очищается, и на основе собранного массива точек создается объект веревки с помощью метода this.add.rope.
- Первые два аргумента (0, 0) — это координаты опорной точки веревки в мире.
- Третий аргумент — ключ текстуры, который циклически меняется от 'pipe1' до 'pipe4'.
- Четвертый аргумент (null) — кадр текстуры, не используется.
- Пятый аргумент — массив объектов Vector2, которые станут контрольными точками веревки.
Созданная веревка добавляется в массив this.ropes для последующей анимации.
const rope = this.add.rope(0, 0, 'pipe' + pipe, null, points);
this.ropes.push(rope);
Анимация точек веревки
В методе update реализована анимация, создающая волнообразное движение. Для каждой веревки в массиве мы перебираем ее точки (rope.points).
Координаты точек модифицируются с помощью синусоидальной функции Math.sin. Аргумент функции зависит от индекса точки (`i) и общего счетчика времени (this.count`). Это создает сдвиг фазы волны вдоль веревки. Амплитуда движения задается умножением на 2.
Условный оператор проверяет свойство rope.horizontal. В нашем примере оно не установлено, поэтому всегда будет выполняться ветка else, анимирующая координату `xи создающая горизонтальную волну. После изменения точек необходимо вызвать методrope.setDirty()`, чтобы сообщить системе рендеринга, что геометрия объекта изменилась и ее нужно перерисовать.
for (let i = 0; i < points.length; i++)
{
if (rope.horizontal)
{
points[i].y += Math.sin(i * 0.15 + this.count) * 2;
}
else
{
points[i].x += Math.sin(i * 0.25 + this.count) * 2;
}
}
rope.setDirty();
Что попробовать дальше
Вы освоили создание интерактивных и анимированных веревок в Phaser 3. Этот пример открывает множество возможностей для экспериментов: попробуйте привязать концы веревки к физическим телам с помощью Matter.js, чтобы создать качающийся мост. Измените логику анимации, чтобы волна распространялась от конкретной точки (например, от места клика). Используйте более длинные текстуры или настраиваемые шейдеры для сегментов, чтобы создать эффект металлического троса или биолюминесцентного щупальца.
