О чем этот пример
В мобильных играх, платформерах и аркадах игровой мир часто превосходит размеры экрана. Чтобы игрок не потерял своего персонажа, камера должна плавно следовать за ним. В Phaser это реализуется парой методов. Разберем пример, где камера следит за кораблем на огромной карте, и научимся настраивать ее поведение, включая плавность слежения и увеличение.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('map', 'assets/tests/camera/earthbound-scarab.png');
this.load.image('ship', 'assets/sprites/fmship.png');
}
create ()
{
this.cameras.main.setBounds(0, 0, 1024, 2048);
this.add.image(0, 0, 'map').setOrigin(0);
this.cursors = this.input.keyboard.createCursorKeys();
this.mode = 0; // 0 = direct, 1 = physics
this.directSpeed = 4.5;
this.ship = this.physics.add.image(400, 300, 'ship');
this.cameras.main.startFollow(this.ship, true);
// this.cameras.main.startFollow(this.ship, true, 0.09, 0.09);
// this.cameras.main.setZoom(2);
this.cameras.main.setZoom(4);
this.input.on('pointerdown', () => {
console.log(this.cameras.main.scrollX, this.cameras.main.scrollY);
console.log(this.cameras.main.matrix);
});
this.events.on('prerender', this.preRender, this);
}
preRender ()
{
// console.log(this.ship.x, this.ship.y, this.cameras.main.scrollX, this.cameras.main.scrollY);
}
update ()
{
if (this.mode === 0)
{
this.updateDirect();
}
else
{
this.updatePhysics();
}
}
updatePhysics ()
{
this.ship.setVelocity(0);
if (this.cursors.left.isDown)
{
this.ship.setAngle(-90).setVelocityX(-200);
}
else if (this.cursors.right.isDown)
{
this.ship.setAngle(90).setVelocityX(200);
}
if (this.cursors.up.isDown)
{
this.ship.setAngle(0).setVelocityY(-200);
}
else if (this.cursors.down.isDown)
{
this.ship.setAngle(-180).setVelocityY(200);
}
}
updateDirect ()
{
if (this.cursors.left.isDown)
{
this.ship.setAngle(-90);
this.ship.x -= this.directSpeed;
}
else if (this.cursors.right.isDown)
{
this.ship.setAngle(90);
this.ship.x += this.directSpeed;
}
if (this.cursors.up.isDown)
{
this.ship.setAngle(0);
this.ship.y -= this.directSpeed;
}
else if (this.cursors.down.isDown)
{
this.ship.setAngle(-180);
this.ship.y += this.directSpeed;
}
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
pixelArt: true,
physics: {
default: 'arcade',
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка мира и камеры
Первым делом, мы создаем игровой мир, который больше области видимости (viewport) камеры. Для этого в методе create() устанавливаем границы для основной камеры (this.cameras.main).
Затем мы добавляем фоновое изображение карты и спрайт корабля, за которым будет следить камера. Корабль создается как физический объект, чтобы позже продемонстрировать два типа управления.
this.cameras.main.setBounds(0, 0, 1024, 2048);
this.add.image(0, 0, 'map').setOrigin(0);
this.ship = this.physics.add.image(400, 300, 'ship');
Включаем слежку камеры за объектом
Сердце примера — метод startFollow(). Он заставляет камеру центрироваться на целевом объекте. Второй аргумент (true) включает линейную интерполяцию (LERP), что делает движение камеры более плавным, а не резким.
За комментированной строкой скрывается расширенная настройка: третий и четвертый аргументы позволяют задать коэффициент сглаживания по осям X и Y. Меньшие значения (например, 0.09) делают слежку более плавной, но с задержкой.
this.cameras.main.startFollow(this.ship, true);
// this.cameras.main.startFollow(this.ship, true, 0.09, 0.09);
Управление зумом камеры
Метод setZoom() изменяет масштаб всего, что отображается камерой. Значение 1 — это 100%, 2 — увеличение в два раза, 0.5 — уменьшение в два раза.
В примере стоит зум 4, что сильно приближает корабль и окружающую его местность. Это полезно для акцента на деталях или создания эффекта боевого режима. Раскомментировав строку с setZoom(2), вы увидите менее агрессивное увеличение.
// this.cameras.main.setZoom(2);
this.cameras.main.setZoom(4);
Два типа управления объектом
В примере реализовано два способа перемещения корабля, переключаемые переменной this.mode. Это наглядно показывает, что камера может следовать за объектом независимо от того, как этот объект перемещается — через прямое изменение координат или с помощью физического движка Arcade.
updateDirect() напрямую меняет свойства `xиyспрайта.updatePhysics()использует методыsetVelocity()` для задания скорости, с которой движок сам изменит положение объекта.
updateDirect() {
if (this.cursors.left.isDown) {
this.ship.setAngle(-90);
this.ship.x -= this.directSpeed;
}
}
updatePhysics() {
this.ship.setVelocity(0);
if (this.cursors.left.isDown) {
this.ship.setAngle(-90).setVelocityX(-200);
}
}
Отладка: смотрим внутреннее состояние камеры
Для понимания того, как работает камера, полезно заглянуть в ее внутренние свойства. В примере по клику мыши (pointerdown) в консоль выводятся текущие координаты скролла камеры и ее матрица трансформации.
Эти данные могут быть полезны для расчета позиции UI-элементов относительно мира или для сложной логики, связанной с видимостью объектов.
this.input.on('pointerdown', () => {
console.log(this.cameras.main.scrollX, this.cameras.main.scrollY);
console.log(this.cameras.main.matrix);
});
Что попробовать дальше
Методы setBounds(), startFollow() и setZoom() дают вам полный контроль над поведением камеры в Phaser, позволяя легко создавать скроллящиеся уровни. Для экспериментов попробуйте: изменить коэффициенты плавности слежки, динамически менять зум в зависимости от скорости корабля, или заставить камеру следовать не за центром, а за смещенной точкой спрайта, используя setFollowOffset().
