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

Создание больших тайловых карт с использованием множественных атласов (Multiatlas) в Phaser позволяет эффективно управлять графикой и оптимизировать производительность вашей игры. Этот подход особенно полезен для генерации процедурных миров, случайного заполнения пространства и работы с большим количеством спрайтов. В статье мы разберем, как загружать multiatlas, получать кадры и использовать их для создания динамической tilemap.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{

    preload ()
    {
        
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
// this.load.setPath('assets/loader-tests/');
        // this.load.multiatlas('megaset', 'texture-packer-multi-atlas.json');

        this.load.multiatlas('megaset', 'assets/loader-tests/texture-packer-multi-atlas.json', 'assets/loader-tests/');
    }

    create ()
    {
        const atlasTexture = this.textures.get('megaset');
        const frames = atlasTexture.getFrameNames();
        const firstFrame = this.textures.getFrame('megaset', frames[20]);

        const rows = 1024;
        const cols = 768;
        const tileSize = 256;

        this.tileMap = this.make.tilemap({ tileWidth: tileSize, tileHeight: firstFrame.height, width: rows, height: cols });

        const tileset = this.tileMap.addTilesetImage('megaset');

        const layer = this.tileMap.createBlankLayer('layer1', tileset);
        layer.forEachTile(tile => {
            const randomIndex = Math.floor(Math.random() * frames.length);
            tile.index = randomIndex;
        });

        console.log(this.tileMap.tilesets);

        // for (let i = 0; i < frames.length; i++)
        // {
        //     const x = Phaser.Math.Between(0, 1024);
        //     const y = Phaser.Math.Between(0, 768);

        //     this.add.image(x, y, 'megaset', frames[ i ]);
        // }
    }
}

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

const game = new Phaser.Game(config);

Загрузка multiatlas в Phaser

Multiatlas — это формат, который позволяет разбивать один большой атлас на несколько файлов изображений, что удобно для управления памятью и загрузки. В Phaser для этого используется метод load.multiatlas. В примере мы загружаем атлас с ключом 'megaset' из JSON-файла и указываем путь к папке с изображениями.

this.load.multiatlas('megaset', 'assets/loader-tests/texture-packer-multi-atlas.json', 'assets/loader-tests/');

Здесь первый аргумент — ключ для доступа к атласу, второй — путь к JSON-файлу с описанием кадров, третий — базовый путь к изображениям. После загрузки атлас становится доступен через текстуры сцены.

Получение кадров из загруженного атласа

После загрузки multiatlas мы можем получить доступ к его кадрам (frames) для использования в тайловой карте. Для этого используем методы this.textures.get и this.textures.getFrameNames.

const atlasTexture = this.textures.get('megaset');
const frames = atlasTexture.getFrameNames();
const firstFrame = this.textures.getFrame('megaset', frames[20]);

atlasTexture — это объект текстуры атласа. frames — массив имен всех кадров в атласе. firstFrame — пример получения конкретного кадра по индексу (в данном случае 20), чтобы узнать его высоту для настройки tilemap. Это полезно, если кадры имеют разный размер.

Создание и заполнение tilemap

Tilemap в Phaser позволяет эффективно отрисовывать большие области с повторяющимися тайлами. В примере мы создаем пустую тайловую карту большого размера и заполняем ее случайными кадрами из multiatlas.

const rows = 1024;
const cols = 768;
const tileSize = 256;

this.tileMap = this.make.tilemap({ tileWidth: tileSize, tileHeight: firstFrame.height, width: rows, height: cols });
const tileset = this.tileMap.addTilesetImage('megaset');
const layer = this.tileMap.createBlankLayer('layer1', tileset);
layer.forEachTile(tile => {
    const randomIndex = Math.floor(Math.random() * frames.length);
    tile.index = randomIndex;
});

make.tilemap создает тайловую карту с указанными параметрами: ширина и высота тайла, общее количество строк и столбцов. addTilesetImage связывает атлас 'megaset' с тайловой картой. createBlankLayer создает слой, а forEachTile проходит по всем тайлам и присваивает каждому случайный индекс из массива кадров. Это позволяет быстро сгенерировать разнообразный ландшафт.

Особенности работы с индексами и кадрами

При использовании multiatlas в tilemap важно понимать, как индексы тайлов соответствуют кадрам в атласе. Каждый кадр в multiatlas имеет свой порядковый номер, который используется как индекс в тайловой карте.

console.log(this.tileMap.tilesets);

Этот выведет в консоль информацию о tileset, включая соответствие индексов и кадров. Убедитесь, что индексы не выходят за пределы количества кадров в атласе, чтобы избежать ошибок отрисовки. В примере случайный индекс генерируется в диапазоне от 0 до frames.length - 1, что гарантирует корректность.

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

Использование multiatlas для создания tilemap в Phaser — мощный инструмент для оптимизации графики и генерации процедурного контента. Вы можете экспериментировать с разными размерами тайлов, комбинировать несколько атласов или добавлять анимацию к тайлам. Попробуйте изменить логику заполнения карты, например, использовать шум Перлина для более естественного распределения или добавлять слои для разных типов местности.