О чем этот пример
Управление камерой — ключевой элемент создания комфортного геймплея. Когда персонаж движется, хочется видеть не только его, но и пространство впереди, чтобы планировать действия. Встроенная камера Phaser с методом `startFollow()` отлично следит за объектом, но по умолчанию центрирует его. В этой статье разберем, как использовать свойство `followOffset` для создания динамического смещения камеры, которое "заглядывает" в сторону движения игрока, как во многих платформерах и топ-даун шутерах. Это простое решение значительно улучшает юзабилити игры.
Версия 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('bg', 'assets/pics/the-end-by-iloe-and-made.jpg');
this.load.image('ship', 'assets/sprites/x2kship.png');
}
create ()
{
// Set the camera and physics bounds to be the size of 4x4 bg images
this.cameras.main.setBounds(0, 0, 1920 * 2, 1080 * 2);
this.physics.world.setBounds(0, 0, 1920 * 2, 1080 * 2);
// Mash 4 images together to create our background
this.add.image(0, 0, 'bg').setOrigin(0);
this.add.image(1920, 0, 'bg').setOrigin(0).setFlipX(true);
this.add.image(0, 1080, 'bg').setOrigin(0).setFlipY(true);
this.add.image(1920, 1080, 'bg').setOrigin(0).setFlipX(true).setFlipY(true);
this.cursors = this.input.keyboard.createCursorKeys();
this.player = this.physics.add.image(400, 300, 'ship');
this.player.setCollideWorldBounds(true);
this.cameras.main.startFollow(this.player);
this.cameras.main.followOffset.set(-300, 0);
}
update ()
{
this.player.setVelocity(0);
if (this.cursors.left.isDown)
{
this.player.setVelocityX(-500);
this.player.setFlipX(true);
this.cameras.main.followOffset.x = 300;
}
else if (this.cursors.right.isDown)
{
this.player.setVelocityX(500);
this.player.setFlipX(false);
this.cameras.main.followOffset.x = -300;
}
if (this.cursors.up.isDown)
{
this.player.setVelocityY(-500);
}
else if (this.cursors.down.isDown)
{
this.player.setVelocityY(500);
}
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
physics: {
default: 'arcade',
},
scene: Example
};
const game = new Phaser.Game(config);
Настройка мира и камеры
Перед тем как заставить камеру следить за игроком со смещением, необходимо подготовить игровое пространство. В примере создается мир, в четыре раза превышающий размер одного фонового изображения, чтобы у игрока было куда двигаться.
this.cameras.main.setBounds(0, 0, 1920 * 2, 1080 * 2);
this.physics.world.setBounds(0, 0, 1920 * 2, 1080 * 2);
Первая строка устанавливает границы для основной камеры (this.cameras.main). Камера не сможет выйти за эти пределы. Вторая строка задает такие же границы для физического мира Arcade, чтобы физическое тело игрока не могло его покинуть.
Далее, из одного изображения собирается большой фон, путем его зеркального отражения и размещения в четырех квадрантах.
Создание игрока и начало слежения
Следующий шаг — создать физический спрайт игрока и включить стандартное слежение камеры за ним.
this.player = this.physics.add.image(400, 300, 'ship');
this.player.setCollideWorldBounds(true);
this.cameras.main.startFollow(this.player);
Метод this.physics.add.image создает спрайт с физическим телом Arcade. setCollideWorldBounds(true) гарантирует, что игрок будет отскакивать от границ мира, которые мы установили ранее. Ключевой вызов — this.cameras.main.startFollow(this.player). Он включает режим преследования, при котором камера плавно перемещается, чтобы целевой объект (игрок) оставался в ее поле зрения.
Без дополнительных параметров камера будет пытаться центрировать игрока. Но мы хотим сместить точку фокуса.
Волшебство свойства followOffset
Смещение реализуется через свойство followOffset объекта камеры. Это объект Phaser.Math.Vector2, который определяет, насколько точка, за которой следует камера, смещена относительно реальных координат цели.
this.cameras.main.followOffset.set(-300, 0);
Эта строка, выполненная в create(), устанавливает начальное смещение. Значение x = -300 означает, что камера будет пытаться удерживать в своем центре не сам корабль игрока, а точку, расположенную на 300 пикселей левее (-300) от его центра. По вертикали смещение равно нулю. Таким образом, в стартовом положении игрок будет смещен вправо от центра камеры, "освобождая" обзор слева.
Динамическое изменение смещения
Статичное смещение полезно, но настоящая магия начинается, когда мы меняем followOffset в реальном времени в зависимости от действий игрока. В методе update() проверяется нажатие клавиш и изменяется смещение по оси X.
if (this.cursors.left.isDown) {
this.player.setVelocityX(-500);
this.player.setFlipX(true);
this.cameras.main.followOffset.x = 300; // Смещаем обзор вправо от игрока
}
else if (this.cursors.right.isDown) {
this.player.setVelocityX(500);
this.player.setFlipX(false);
this.cameras.main.followOffset.x = -300; // Смещаем обзор влево от игрока
}
Логика проста: если игрок движется влево, мы хотим видеть пространство справа от него (по направлению движения). Поэтому мы устанавливаем followOffset.x = 300. Теперь камера фокусируется на точке, которая находится на 300 пикселей *правее* центра корабля, эффективно смещая видимую область вправо и открывая обзор в сторону движения. При движении вправо работает обратная логика. Смещение меняется мгновенно, что дает четкий и отзывчивый визуальный фидбек.
Важные нюансы реализации
1. **Порядок операций:** Сначала вызывается startFollow(), и только потом устанавливается followOffset. Попытка задать смещение до начала слежения не даст эффекта.
2. **Поведение у границ:** Камера с включенными границами (setBounds) будет корректно обрабатывать смещение. Когда игрок подходит к краю мира, камера перестает следовать за смещенной точкой, чтобы не показывать пустоту за пределами уровня.
3. **Плавность:** Камера main по умолчанию использует плавное (линейное) интерполированное слежение. Свойство followOffset интегрируется в этот расчет, поэтому смещение также применяется плавно, без рывков.
4. **Сброс смещения:** В данном примере смещение по оси X сбрасывается только при движении влево/вправо. Если нужен центральный обзор при остановке, можно добавить условие для сброса followOffset.x в `0` при отсутствии горизонтального движения.
Что попробовать дальше
Свойство followOffset — мощный и простой инструмент для создания продвинутого поведения камеры. Оно позволяет уводить фокус от персонажа, улучшая игровое предвидение. Для экспериментов попробуйте: изменять смещение не мгновенно, а с плавной интерполяцией; привязать величину смещения к скорости игрока; использовать followOffset.y при вертикальном движении для взгляда вверх/вниз, как в играх с прыжками. Это небольшое изменение может кардинально повысить комфорт управления в вашем проекте.
