О чем этот пример
При разработке игр часто возникает задача создать группу объектов, которые движутся не хаотично, а слаженно, как стая птиц или косяк рыб. Phaser предоставляет мощный инструментарий для физики, но для плавного, органичного движения иногда требуется дополнительная обработка. В этой статье мы разберем, как использовать линейную интерполяцию скорости в Arcade Physics для создания эффекта "следования" объектов друг за другом, что идеально подходит для симуляции стайного поведения.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
fishes;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('sea', 'assets/pics/undersea.jpg');
this.load.spritesheet('fish', 'assets/sprites/fish-136x80.png', { frameWidth: 136, frameHeight: 80 });
}
create ()
{
this.add.image(400, 300, 'sea');
this.fishes = this.physics.add.group({
key: 'fish',
frame: [ 0, 1, 2 ],
repeat: 1,
setXY: { x: 400, y: 300 },
bounceX: 1,
bounceY: 1,
collideWorldBounds: true
});
this.fishes.getChildren()[0].setVelocity(100, -200);
}
update ()
{
let prev;
for (const fish of this.fishes.getChildren())
{
fish.rotation = fish.body.angle;
if (prev)
{
fish.body.velocity.lerp(prev.body.velocity, 0.05);
}
prev = fish;
}
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
debug: false
}
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и создание группы
Класс Example расширяет Phaser.Scene. В методе preload загружаются фон и спрайтшит с тремя кадрами рыбы.
В методе create мы добавляем фон и создаем физическую группу объектов. Группа — это удобный способ управлять множеством однотипных объектами. Ключевые параметры конфигурации:
this.fishes = this.physics.add.group({
key: 'fish',
frame: [ 0, 1, 2 ], // Используем три разных кадра из спрайтшита
repeat: 1, // Создаем по одному экземпляру на каждый кадр
setXY: { x: 400, y: 300 }, // Начальная позиция для всех
bounceX: 1,
bounceY: 1, // Объекты отскакивают от границ мира
collideWorldBounds: true
});
После создания группы первому объекту в ней вручную задается начальная скорость с помощью setVelocity. Остальные объекты пока неподвижны.
Магия интерполяции в методе Update
Вся логика связывания движения происходит в методе update, который вызывается на каждом кадре. Здесь мы перебираем всех рыб в группе.
for (const fish of this.fishes.getChildren())
{
fish.rotation = fish.body.angle;
if (prev)
{
fish.body.velocity.lerp(prev.body.velocity, 0.05);
}
prev = fish;
}
1. fish.rotation = fish.body.angle; — синхронизирует графический поворот спрайта с углом, рассчитанным физическим движком (это важно, так как тело может вращаться при столкновениях).
2. Переменная prev хранит предыдущую рыбу в цепочке.
3. Для каждой рыбы, кроме первой, мы вызываем метод lerp (Linear intERPolation) на векторе ее скорости fish.body.velocity.
Как работает метод lerp
Метод lerp(target, amount) — это ключевой элемент примера. Он постепенно сближает текущий вектор (скорость рыбы) с целевым вектором (скоростью предыдущей рыбы).
- **Первый аргумент (target)**: целевой вектор скорости, к которому мы стремимся. В нашем случае — это скорость предыдущей рыбы в списке.
- **Второй аргумент (amount)**: коэффициент интерполяции (в примере 0.05). Значение 0.0 оставит скорость неизменной, 1.0 мгновенно заменит ее на целевую. Значение 0.05 означает, что на каждом кадре скорость текущей рыбы будет на 5% ближе к скорости предыдущей.
Таким образом, вторая рыба плавно начинает повторять движение первой, третья — второй, и так далее. Это создает красивый волнообразный, запаздывающий эффект движения стаи от ведущего объекта.
Настройка физического мира
Для работы примера необходима правильная конфигурация игры. В объекте config активируется Arcade Physics.
physics: {
default: 'arcade',
arcade: {
debug: false // Включите true для отладки границ тел
}
},
Именно движок arcade предоставляет объектам свойство body с векторами velocity и методом lerp. Без активированной физики этот код не будет работать.
Что попробовать дальше
Использование velocity.lerp() — это простой, но мощный прием для создания сложного на вид группового поведения. Он позволяет избежать жесткой привязки позиций, обеспечивая более естественное, физически правдоподобное движение.
**Идеи для экспериментов:**
1. Измените коэффициент 0.05 на большее значение (например, 0.2), чтобы увидеть, как рыбы становятся более «резвыми» и быстрее синхронизируются.
2. Попробуйте применить интерполяцию не к скорости, а к позиции (fish.x) для другого визуального эффекта.
3. Сделайте первую рыбу управляемой с клавиатуры или курсором мыши, чтобы вести за собой всю стаю.
