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

Phaser предоставляет мощные инструменты для организации загрузки ресурсов. Однако при работе с JSON-паками не всегда очевидно, как загрузить не весь пакет целиком, а лишь его часть. Эта статья на практическом примере показывает, как использовать третий, необязательный параметр `dataKey` в методе `this.load.pack()`, чтобы точечно управлять загрузкой ассетов. Вы научитесь структурировать большие наборы ресурсов и загружать только то, что нужно для конкретной сцены или игрового режима.

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

Живой запуск

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

Исходный код


const data = {
    test1: {
        files: [
            {
                type: 'image',
                key: 'beer',
                url: 'assets/sprites/beer.png'
            },
            {
                type: 'image',
                key: 'donut',
                url: 'assets/sprites/donut.png'
            },
        ]
    },
    test2: {
        files: [
            {
                type: 'image',
                key: 'ginger',
                url: 'assets/sprites/gingerbread.png'
            },
            {
                type: 'image',
                key: 'hotdog',
                url: 'assets/sprites/hotdog.png'
            },
        ]
    }
}

class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        // this.load.pack('pack', data);
        this.load.pack('pack', data, 'test1');
        // this.load.json('bob', data, 'test2');
    }

    create ()
    {
        // console.log(this.cache.json.get('bob'));

        this.add.image(400, 300, 'beer');
        // this.add.image(400, 300, 'hotdog');
    }
}

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

const game = new Phaser.Game(config);

Проблема: один JSON, много ресурсов

Представьте, что вы создаёте масштабную игру, где ассеты логически разделены по уровням, локациям или персонажам. Хранить ссылки на все файлы в одном месте удобно для управления, но загружать всё сразу — неэффективно. Это увеличивает время начальной загрузки и расходует память.

Исходный код демонстрирует как раз такую структуру данных. В объекте data есть два вложенных объекта (test1 и test2), каждый из которых содержит свой массив файлов (files).

Решение: параметр dataKey в load.pack

Метод this.load.pack() имеет следующую сигнатуру:

this.load.pack(key, url, dataKey, xhrSettings)

Третий параметр, dataKey, — это строка, указывающая на конкретный ключ внутри вашего JSON-объекта. Phaser загрузит только ту часть данных, которая находится по этому ключу.

В примере, чтобы загрузить только изображения из раздела test1 (пиво и пончик), используется вызов:

this.load.pack('pack', data, 'test1');

Без указания dataKey Phaser попытается интерпретировать весь объект data как конфигурацию пакета, что может привести к ошибке, если структура не соответствует ожидаемой для корневого уровня пакета.

Сравнение с load.json

Важно не путать load.pack с load.json. Метод load.json загружает или обрабатывает JSON-файл как данные, доступные через кэш (this.cache.json). Он не запускает автоматическую загрузку файлов, описанных внутри этого JSON.

Закомментированная строка в примере показывает этот подход:

// this.load.json('bob', data, 'test2');

После такой загрузки объект data['test2'] стал бы доступен через this.cache.json.get('bob'). Однако изображения ginger и hotdog не были бы загружены в текстуры, и попытка создать спрайт с ключом hotdog вызвала бы ошибку. Метод pack делает больше — он не только сохраняет данные, но и ставит файлы из files в очередь на загрузку.

Практическое применение в сцене

В методе create() сцены создаётся спрайт, используя ключ beer, который был определён и загружен благодаря вызову load.pack('pack', data, 'test1').

this.add.image(400, 300, 'beer');

Если бы мы загружали раздел test2 и попытались создать спрайт hotdog, он бы отобразился корректно. Попытка же использовать незагруженный ключ (например, раскомментировав строку с hotdog, не загрузив test2) привела бы к ошибке в консоли и пустому месту на экране.

Этот механизм идеально подходит для загрузки ресурсов для конкретного уровня игры прямо перед его стартом в методе preload() сцены этого уровня.

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

Использование параметра dataKey в this.load.pack() — это мощный паттерн для модульной загрузки ресурсов. Он позволяет держать все описания ассетов в одном JSON-файле, но загружать их порционно, по мере необходимости. Для экспериментов попробуйте: 1. Создать структуру данных с разделами для разных типов врагов или боссов и загружать их пакетно перед битвой. 2. Динамически определять, какой dataKey загружать, на основе выбора игрока или прогресса. 3. Комбинировать несколько вызовов load.pack с разными ключами в одной сцене для сборки необходимого набора ассетов.