О чем этот пример
Мини-карты — неотъемлемый элемент многих игр, от RPG до стратегий. Они помогают игрокам ориентироваться на большой локации, не теряя общий контекст. В Phaser 3 вы можете легко добавить вторую камеру, которая будет служить такой мини-картой, и анимировать её для создания динамического эффекта обзора или слежения. Эта статья покажет, как создать отдельную камеру-миниатюру, настроить её вид и заставить плавно перемещаться по сгенерированной карте. Этот приём полезен не только для статических карт, но и для отображения положения других игроков, важных объектов или анимации кат-сцен.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
height = 38;
width = 40;
t = 0;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('tiles', 'assets/tilemaps/tiles/drawtiles.png');
}
create ()
{
// Build a random level as a 2D array
const level = [];
for (let y = 0; y < this.height; y++)
{
const row = [];
for (let x = 0; x < this.width; x++)
{
const tileIndex = Phaser.Math.RND.integerInRange(0, 6);
row.push(tileIndex);
}
level.push(row);
}
const map = this.make.tilemap({ data: level, tileWidth: 32, tileHeight: 32 });
const tileset = map.addTilesetImage('tiles');
const layer = map.createLayer(0, tileset, 0, 0);
this.cameras.main.setBounds(0, 0, layer.width, layer.height);
this.minimap = this.cameras.add(200, 10, 100, 100).setZoom(0.2);
this.minimap.setBackgroundColor('#ffff00');
}
update ()
{
this.minimap.scrollX = this.width * 32 / 2 + Math.cos(this.t) * 300;
this.minimap.scrollY = this.height * 32 / 2 + Math.sin(this.t) * 300;
this.t += 0.025;
}
}
const config = {
width: 800,
height: 600,
resolution: 1,
type: Phaser.AUTO,
parent: 'phaser-example',
pixelArt: true,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ассетов
В классе сцены определяются базовые параметры: высота и ширина будущей карты в тайлах, а также переменная `t` для анимации.
В методе preload() загружается единственный tileset — изображение, содержащее все виды тайлов. Обратите внимание на использование setBaseURL() — это удобный способ указать базовый путь для загрузки ресурсов, если они хранятся удалённо.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('tiles', 'assets/tilemaps/tiles/drawtiles.png');
}
Генерация случайной карты и создание слоя
Вся логика создания игрового мира сосредоточена в create(). Сначала вручную генерируется двумерный массив level, представляющий карту. Каждый элемент массива — случайное число от 0 до 6, что соответствует индексу тайла в tileset.
const level = [];
for (let y = 0; y < this.height; y++)
{
const row = [];
for (let x = 0; x < this.width; x++)
{
const tileIndex = Phaser.Math.RND.integerInRange(0, 6);
row.push(tileIndex);
}
level.push(row);
}
Затем этот массив преобразуется в объект Tilemap с помощью this.make.tilemap(). Важно указать размер тайла. После создания tileset на основе загруженного изображения, формируется визуальный слой layer.
const map = this.make.tilemap({ data: level, tileWidth: 32, tileHeight: 32 });
const tileset = map.addTilesetImage('tiles');
const layer = map.createLayer(0, tileset, 0, 0);
Настройка основной камеры и создание мини-карты
Основной камере задаются границы (setBounds), равные размерам созданного слоя. Это предотвращает её выход за пределы игрового мира.
Самое интересное — создание второй камеры, которая будет мини-картой. Это делается методом this.cameras.add(), куда передаются координаты, ширина и высота новой камеры на экране.
Ключевой параметр — setZoom(0.2). Он уменьшает масштаб отображения, позволяя уместить большую карту в маленький прямоугольник. Фон мини-карты подсвечивается жёлтым цветом для наглядности.
this.cameras.main.setBounds(0, 0, layer.width, layer.height);
this.minimap = this.cameras.add(200, 10, 100, 100).setZoom(0.2);
this.minimap.setBackgroundColor('#ffff00');
Анимация движения мини-карты
Чтобы мини-карта не была статичной, в методе update() реализовано её плавное движение по кругу. Это имитирует, например, автоматический обзор локации.
Переменная this.t плавно увеличивается, выступая в роли угла. Синус и косинус от этого угла, умноженные на радиус (300), задают смещение относительно центра карты. Центр рассчитывается как половина от общего размера мира в пикселях (this.width * 32 / 2).
update ()
{
this.minimap.scrollX = this.width * 32 / 2 + Math.cos(this.t) * 300;
this.minimap.scrollY = this.height * 32 / 2 + Math.sin(this.t) * 300;
this.t += 0.025;
}
Изменяя свойства scrollX и scrollY мини-камеры, мы управляем той областью игрового мира, которая в неё попадает.
Конфигурация игры
Код завершается стандартной конфигурацией игры. Обратите внимание на два важных параметра:
- pixelArt: true — включает сглаживание текстур для пиксельной графики.
- scene: Example — указывает класс сцены, который будет запущен.
const config = {
width: 800,
height: 600,
resolution: 1,
type: Phaser.AUTO,
parent: 'phaser-example',
pixelArt: true,
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Вы создали динамическую мини-карту, которая является отдельной камерой со своим zoom и положением. Это мощный паттерн. Для экспериментов попробуйте
- Привязать движение мини-камеры не к синусу, а к координатам спрайта игрока для классического слежения
- Добавить на мини-карту маркеры (
Graphics) для обозначения ключевых точек - Сделать мини-карту интерактивной, обрабатывая клики по ней для перемещения основной камеры
