О чем этот пример
При создании игр с большими уровнями или скроллингом возникает задача управления видимой областью игрового мира. Phaser решает её с помощью системы камер и мировых координат. В этой статье мы разберем, как настроить камеру для следования за игроком по большому игровому миру и в чём разница между координатами экрана и координатами мира. Это знание — основа для создания платформеров, RPG и других игр с обширными локациями.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
text;
cursors;
player;
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.startFollow(this.player, true, 0.05, 0.05);
this.text = this.add.text(10, 10, 'Cursors to move', { font: '16px Courier', fill: '#00ff00' }).setScrollFactor(0);
}
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);
}
this.text.setText([
`screen x: ${this.input.x}`,
`screen y: ${this.input.y}`,
`world x: ${this.input.mousePointer.worldX}`,
`world y: ${this.input.mousePointer.worldY}`
]);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
physics: {
default: 'arcade'
},
scene: Example
};
const game = new Phaser.Game(config);
Создание большого игрового мира
Первый шаг — определение границ, в которых может существовать игрок и камера. В примере мир в четыре раза больше исходного фонового изображения. Устанавливаются границы камеры (cameras.main.setBounds) и границы физического мира (physics.world.setBounds). Это разные сущности: границы камеры ограничивают область, которую она может показывать, а границы физического мира определяют, с чем будет сталкиваться физическое тело.
this.cameras.main.setBounds(0, 0, 1920 * 2, 1080 * 2);
this.physics.world.setBounds(0, 0, 1920 * 2, 1080 * 2);
Далее создаётся фон, составленный из четырёх изображений. Каждое изображение смещается и зеркально отражается (методы .setFlipX(true) и .setFlipY(true)), чтобы заполнить весь заданный мир без швов.
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);
Игрок, управление и привязка камеры
Создаётся физический спрайт игрока и настраивается управление с клавиатуры. Важный момент — игроку задаётся столкновение с границами мира (setCollideWorldBounds(true)), чтобы он не мог выйти за пределы уровня.
this.player = this.physics.add.image(400, 300, 'block');
this.player.setCollideWorldBounds(true);
Самое важное для камеры — метод startFollow. Он заставляет камеру следовать за заданным игровым объектом. Параметры 0.05, 0.05 задают линейную интерполяцию (Lerp), делая движение камеры плавным, с небольшим отставанием.
this.cameras.main.startFollow(this.player, true, 0.05, 0.05);
Текст с инструкциями создаётся с параметром setScrollFactor(0). Это означает, что текст будет оставаться на одном месте экрана, не двигаясь вместе с камерой, что идеально для интерфейса.
Разница между координатами экрана и мира
В методе update происходит обновление позиции игрока в зависимости от нажатых клавиш. Но ключевая демонстрация — это вывод двух пар координат в текстовом поле.
this.text.setText([
`screen x: ${this.input.x}`,
`screen y: ${this.input.y}`,
`world x: ${this.input.mousePointer.worldX}`,
`world y: ${this.input.mousePointer.worldY}`
]);
* **Координаты экрана (`this.input.x`, `this.input.y`)**: Это позиция курсора мыши (или касания) относительно окна игры. Если вы переместите курсор в левый верхний угол окна, значения будут близки к (0,0), независимо от того, куда сейчас направлена камера в игровом мире.
* **Мировые координаты (`this.input.mousePointer.worldX`, `this.input.mousePointer.worldY`)**: Это позиция курсора, пересчитанная в координаты игрового мира. Когда камера смещается, эти координаты меняются, указывая, на какой именно объект мира вы сейчас навели курсор. Именно эти координаты используются для взаимодействия с игровыми объектами (например, для выстрела или выбора цели).
Конфигурация игры и физики
Для работы примера необходима базовая конфигурация игры. Ключевой момент — активация физического движка Arcade, который позволяет использовать методы physics.add.image и setVelocity.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
physics: {
default: 'arcade' // Включаем физический движок Arcade
},
scene: Example
};
const game = new Phaser.Game(config);
Без указания physics: { default: 'arcade' } вызов this.physics.add.image вызовет ошибку, а методы setVelocity и setCollideWorldBounds не будут работать.
Что попробовать дальше
Понимание разницы между экранными и мировыми координатами, а также умение управлять камерой — критически важные навыки для разработки игр в Phaser. Камера, следующая за игроком, и статический интерфейс — это базовые паттерны. Для экспериментов попробуйте: изменить параметры интерполяции в startFollow для более резкого или плавного слежения; добавить зум камеры с помощью setZoom; создать вторую камеру для мини-карты или другого ракурса с помощью cameras.add. Это откроет путь к созданию сложных и визуально интересных игровых пространств.
