О чем этот пример

Создание игровых уровней с помощью тайлмапов — основа 2D-разработки. Этот пример демонстрирует, как загружать карту, созданную в Tiled Editor, и настраивать плавное управление камерой с клавиатуры. Вы научитесь корректно работать с тайлами разного разрешения и ограничивать движение камеры в пределах уровня, что критично для большинства игр с боковым или видом сверху.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    controls;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('tiles', 'assets/tilemaps/tiles/dangerous-kiss-x2.png');
        this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/dangerous-kiss.json');
    }

    create ()
    {
        const map = this.make.tilemap({ key: 'map' });

        // The map was created with 8x8 tiles, but we want to load it with a 2x high resolution tileset
        map.setBaseTileSize(16, 16);

        const tileset = map.addTilesetImage('DangerousKiss_bank.png', 'tiles', 16, 16);

        const layer = map.createLayer('ShoeBox Tile Grab', tileset, 0, 0);

        const cursors = this.input.keyboard.createCursorKeys();

        const controlConfig = {
            camera: this.cameras.main,
            left: cursors.left,
            right: cursors.right,
            up: cursors.up,
            down: cursors.down,
            acceleration: 0.04,
            drag: 0.0005,
            maxSpeed: 0.7
        };

        this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);

        this.cameras.main.setBounds(0, 0, layer.width, layer.height);
    }

    update (time, delta)
    {
        this.controls.update(delta);
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d88',
    parent: 'phaser-example',
    pixelArt: true,
    scene: Example
};

const game = new Phaser.Game(config);

Загрузка ресурсов и настройка тайлмапа

В методе preload загружаются изображение тайлсета и JSON-файл с картой, созданной в Tiled.

Ключевой момент происходит в create. Объект тайлмапа создается через this.make.tilemap. Карта в Tiled была сделана с базовым размером тайла 8x8 пикселей, но мы используем изображение с удвоенным разрешением (16x16). Метод map.setBaseTileSize(16, 16) сообщает движку о новом физическом размере тайла для корректного отображения.

const map = this.make.tilemap({ key: 'map' });
map.setBaseTileSize(16, 16);
const tileset = map.addTilesetImage('DangerousKiss_bank.png', 'tiles', 16, 16);
const layer = map.createLayer('ShoeBox Tile Grab', tileset, 0, 0);

Настройка плавного управления камерой

Для перемещения по большой карте используется Phaser.Cameras.Controls.SmoothedKeyControl. Этот контроллер обеспечивает плавное, инерционное движение камеры при нажатии клавиш, что гораздо приятнее для игрока, чем мгновенные «телепортации».

Конфигурационный объект связывает клавиши-стрелки с камерой и задает параметры движения: ускорение (acceleration), сопротивление (drag) и максимальную скорость (maxSpeed).

const controlConfig = {
    camera: this.cameras.main,
    left: cursors.left,
    right: cursors.right,
    up: cursors.up,
    down: cursors.down,
    acceleration: 0.04,
    drag: 0.0005,
    maxSpeed: 0.7
};
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);

Ограничение области видимости камеры

Чтобы камера не выходила за пределы игрового уровня, необходимо задать её границы. Это делается с помощью метода setBounds. В качестве границ передаются координаты (0, 0) и размеры всего слоя с тайлами (layer.width, layer.height).

this.cameras.main.setBounds(0, 0, layer.width, layer.height);

После этого, как бы игрок ни двигал камеру, она не будет показывать пустоту за краями карты.

Обновление состояния контроллера

Логика плавного движения камеры требует постоянного обновления. Контроллер SmoothedKeyControl имеет метод update, который должен вызываться каждый кадр в основном цикле игры. В него передается дельта-время (delta), прошедшее с предыдущего кадра, чтобы движение оставалось плавным и независимым от частоты кадров.

update (time, delta)
{
    this.controls.update(delta);
}

Конфигурация игры и важность pixelArt

В конфигурации игры обратите внимание на параметр pixelArt: true. При включении этой опции Phaser отключает линейную интерполяцию текстур при их масштабировании. Это критически важно для пиксель-арт графики, как в нашем примере, так как сохраняет чёткие, «квадратные» пиксели без размытия.

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d88',
    parent: 'phaser-example',
    pixelArt: true, // Запрещает сглаживание пиксель-арта
    scene: Example
};

Что попробовать дальше

Этот пример даёт готовую основу для просмотра больших тайлмаповых уровней. Для экспериментов попробуйте изменить параметры acceleration, drag и maxSpeed у контроллера, чтобы получить ощущение «скользкой» или, наоборот, «жёсткой» камеры. Также можно добавить зум камеры через this.cameras.main.setZoom и привязать его к колёсику мыши.