О чем этот пример
Одна из ключевых задач при создании игр с большим миром — управление камерой. Если игрок может выходить за пределы одного экрана, камера должна плавно следовать за ним, чтобы игрок всегда оставался в центре внимания. В этой статье мы разберем пример из официальной документации 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('bg', 'assets/pics/the-end-by-iloe-and-made.jpg');
this.load.image('block', 'assets/sprites/block.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, 'block');
this.player.setCollideWorldBounds(true);
this.cameras.main.setRoundPixels(true);
this.cameras.main.startFollow(this.player, true);
// this.cameras.main.startFollow(this.player, true, 0.05, 0.05);
this.events.on('prerender', this.preRender, this);
}
preRender ()
{
// console.log(this.player.x, this.player.y, this.cameras.main.scrollX, this.cameras.main.scrollY);
}
update ()
{
this.player.setVelocity(0);
if (this.cursors.left.isDown)
{
this.player.setVelocityX(-500);
}
else if (this.cursors.right.isDown)
{
this.player.setVelocityX(500);
}
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);
Подготовка мира: границы камеры и физики
Перед тем как заставить камеру следовать за игроком, необходимо определить границы игрового мира. Это важно, чтобы камера знала, где ей можно перемещаться, а физическое тело игрока не вылетало за пределы уровня.
В методе create() устанавливаются границы для камеры и физического мира. Они задаются одинаковыми, чтобы перемещение камеры соответствовало реальной геометрии уровня.
this.cameras.main.setBounds(0, 0, 1920 * 2, 1080 * 2);
this.physics.world.setBounds(0, 0, 1920 * 2, 1080 * 2);
Здесь создается мир размером 3840x2160 пикселей (удвоенное разрешение Full HD). Фон, состоящий из четырёх склеенных изображений, идеально заполняет эти границы.
Создание и настройка игрока
Игрок создается как физический спрайт с помощью фабрики this.physics.add.image. Это автоматически добавляет ему тело Arcade Physics, которое может сталкиваться с границами мира и получать скорость.
this.player = this.physics.add.image(400, 300, 'block');
this.player.setCollideWorldBounds(true);
Метод setCollideWorldBounds(true) включает столкновение спрайта с границами физического мира, которые мы установили ранее. Это не даст игроку вылететь за пределы уровня.
Управление осуществляется через курсорные клавиши. В методе update() мы каждый кадр сбрасываем скорость игрока на ноль, а затем, в зависимости от нажатых клавиш, задаем новую скорость по осям X и Y.
this.player.setVelocity(0);
if (this.cursors.left.isDown) {
this.player.setVelocityX(-500);
}
Волшебный метод: startFollow
Основная магия происходит в одной строчке. Метод this.cameras.main.startFollow() заставляет главную камеру следовать за указанной целью — в нашем случае, за спрайтом игрока.
this.cameras.main.startFollow(this.player, true);
Первый параметр — это цель слежения. Второй логический параметр lerp (линейная интерполяция) определяет, будет ли камера двигаться к цели мгновенно (false) или плавно, с небольшим запаздыванием (true). В нашем примере включен плавный режим.
В закомментированной строке показаны дополнительные параметры для тонкой наки:
// this.cameras.main.startFollow(this.player, true, 0.05, 0.05);
Третий и четвертый параметры — это коэффициенты интерполяции по осям X и Y. Чем они меньше, тем сильнее запаздывание и плавнее движение камеры. Значения 0.05 создадут очень плавный, "ленивый" эффект следования.
Важные детали: округление пикселей и событие prerender
Два менее очевидных, но полезных приема также используются в примере.
this.cameras.main.setRoundPixels(true);
Метод setRoundPixels(true) включает округление координат отрисовки до целых пикселей. Это помогает избежать размытия спрайтов при их субпиксельном позиционировании, что особенно важно для пиксель-арт игр или при использовании камеры, которая часто останавливается на дробных координатах.
Также в сцену добавлен слушатель события prerender:
this.events.on('prerender', this.preRender, this);
Это событие срабатывает каждый кадр перед отрисовкой сцены. В предоставленном методе preRender() закомментирован вывод в консоль, который может быть полезен для отладки: он показывает текущие координаты игрока (this.player.x, this.player.y) и позицию прокрутки камеры (this.cameras.main.scrollX, this.cameras.main.scrollY). Вы можете раскомментировать console.log, чтобы в реальном времени наблюдать за этими значениями и лучше понимать работу камеры.
Что попробовать дальше
Настройка следящей камеры в Phaser сводится к правильной установке границ мира и вызову startFollow(). Используя параметры интерполяции, можно добиться эффекта камеры от плавного и ленивого до мгновенного и резкого, что напрямую влияет на ощущения от геймплея. Для экспериментов попробуйте: изменить коэффициенты интерполяции в startFollow() на другие значения; отключить lerp, чтобы камера телепортировалась за игроком; или привязать камеру не к самому спрайту игрока, а к точке слегка впереди него по направлению движения, чтобы камера "заглядывала" вперед.
