О чем этот пример
В мобильной и веб-разработке производительность часто упирается в размер и скорость загрузки графики. Использование сжатых текстур (ASTC, ETC2, PVRTC, S3TC) — ключевой метод для оптимизации игр, особенно под платформы с ограниченной пропускной способностью или памятью. Этот пример демонстрирует, как Phaser 3 через загрузчик `pack` позволяет гибко и элегантно загружать одну и ту же текстуру в разных форматах сжатия, автоматически выбирая подходящий для текущего браузера или устройства. Это избавляет разработчика от необходимости писать сложные условия и вручную проверять поддержку кодеков.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
var FilePackObject = {
"pack": {
"path": "assets/compressed",
"files": [
{
"type": "texture",
"key": "labs",
"url": {
"ASTC": { type: "PVR", textureURL: 'compressed/textures-ASTC-4x4-lRGB.pvr', atlasURL: 'uncompressed/textures.json' },
"ETC": { type: "PVR", textureURL: 'compressed/textures-ETC2-lRGB.pvr', atlasURL: 'uncompressed/textures.json' },
"PVRTC": { type: "PVR", textureURL: 'compressed/textures-PVRTC-4BPP-lRGB.pvr', atlasURL: 'uncompressed/textures.json' },
"S3TC": { type: "PVR", textureURL: 'compressed/textures-S3TC-BC3-lRGB.pvr', atlasURL: 'uncompressed/textures.json' },
"IMG": { textureURL: 'textures.png', atlasURL: 'uncompressed/textures.json' }
}
}
]
},
"meta": {
"generated": "1401380327373",
"app": "Phaser 3 Asset Packer",
"url": "https://phaser.io",
"version": "1.0",
"copyright": "Photon Storm Ltd. 2021"
}
};
class Demo extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.pack('pack1', FilePackObject);
}
create ()
{
const bubble = this.add.sprite(0, 40, 'labs', 'bubble256').setOrigin(0);
const logo = this.add.sprite(80, 100, 'labs', 'logo').setOrigin(0);
this.add.text(400, 570, logo.frame.source.compressionAlgorithm).setOrigin(0.5, 0);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#2d2d6d',
scene: Demo
};
const game = new Phaser.Game(config);
Структура объекта FilePackObject
Вся конфигурация ассетов упакована в объект FilePackObject. Это JSON-структура, которую понимает загрузчик this.load.pack(). Она состоит из двух основных частей: pack и meta.
Объект pack содержит путь (path) и массив файлов (files). В данном случае загружается всего один ассет типа "texture" с ключом "labs". Самое важное — это свойство url, которое представляет собой объект с ключами для разных форматов сжатия ("ASTC", "ETC", "PVRTC", "S3TC") и резервного формата "IMG" (обычный PNG).
{
"type": "texture",
"key": "labs",
"url": {
"ASTC": { type: "PVR", textureURL: 'compressed/textures-ASTC-4x4-lRGB.pvr', atlasURL: 'uncompressed/textures.json' },
"ETC": { type: "PVR", textureURL: 'compressed/textures-ETC2-lRGB.pvr', atlasURL: 'uncompressed/textures.json' },
"PVRTC": { type: "PVR", textureURL: 'compressed/textures-PVRTC-4BPP-lRGB.pvr', atlasURL: 'uncompressed/textures.json' },
"S3TC": { type: "PVR", textureURL: 'compressed/textures-S3TC-BC3-lRGB.pvr', atlasURL: 'uncompressed/textures.json' },
"IMG": { textureURL: 'textures.png', atlasURL: 'uncompressed/textures.json' }
}
}
Каждый формат содержит два URL: textureURL (путь к сжатому файлу изображения, например, .pvr) и atlasURL (путь к JSON-файлу атласа, описывающему кадры). Для форматов сжатия также указан type: "PVR", что сообщает загрузчику, как обрабатывать файл. Формат "IMG" не имеет типа, что подразумевает загрузку обычного PNG-изображения. Phaser автоматически проверит поддержку браузером каждого формата и загрузит первый подходящий, начиная с самого эффективного.
Настройка загрузчика и загрузка пака
В методе preload() сцены Demo происходит настройка базового URL и непосредственная загрузка пака ассетов.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.pack('pack1', FilePackObject);
}
Сначала this.load.setBaseURL() задаёт корневой путь для всех последующих загрузок. Это удобно, чтобы не писать полные URL в каждом описании ассета.
Затем вызывается this.load.pack('pack1', FilePackObject). Первый аргумент — это произвольный ключ для пакета (может быть полезен, если пакетов несколько). Второй аргумент — наш объект конфигурации. Загрузчик Phaser проанализирует объект FilePackObject, проверит поддержку форматов сжатия текстуры в текущей среде выполнения и выберет оптимальный вариант. Если ни один из форматов сжатия (ASTC, ETC2 и т.д.) не поддерживается, будет использован резервный вариант "IMG" с PNG-изображением. Вся эта логика выполняется "под капотом", скрывая от разработчика сложности кроссплатформенной совместимости.
Создание спрайтов и вывод информации
После успешной загрузки в методе create() мы можем использовать текстуру с ключом 'labs', как если бы это была обычная текстура.
create ()
{
const bubble = this.add.sprite(0, 40, 'labs', 'bubble256').setOrigin(0);
const logo = this.add.sprite(80, 100, 'labs', 'logo').setOrigin(0);
this.add.text(400, 570, logo.frame.source.compressionAlgorithm).setOrigin(0.5, 0);
}
Первые две строки создают спрайты из загруженного атласа. this.add.sprite(x, y, 'labs', 'bubble256') создаёт спрайт, использующий текстуру с ключом 'labs' и конкретный кадр (frame) из атласа с именем 'bubble256'. Аналогично создаётся спрайт с кадром 'logo'. Метод .setOrigin(0) устанавливает точку привязки спрайта в его левый верхний угол.
Самая интересная часть — последняя строка: this.add.text(... logo.frame.source.compressionAlgorithm ...). Здесь мы получаем доступ к объекту кадра (logo.frame), его источнику (source — это сама текстура) и свойству compressionAlgorithm. Phaser автоматически записывает в это свойство строку с названием алгоритма сжатия, который был фактически использован при загрузке текстуры (например, "ASTC", "ETC", "PVRTC" или "NONE" для несжатого PNG). Этот текст выводится на экран, что позволяет в реальном времени видеть, какой формат сработал в вашем браузере.
Конфигурация игры и запуск
Стандартная конфигурация игры Phaser указывает на нашу сцену Demo.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#2d2d6d',
scene: Demo
};
const game = new Phaser.Game(config);
Здесь нет ничего специфичного для загрузки сжатых текстур. Конфигурация обычная: задаётся тип рендерера (Phaser.AUTO), родительский DOM-элемент (parent), размеры холста, цвет фона и основная сцена. Важно, что вся магия с выбором формата текстуры происходит на этапе preload(), до создания игровых объектов. После создания экземпляра new Phaser.Game(config) запускается жизненный цикл сцены: init(), preload(), create(), update().
Что попробовать дальше
Пример наглядно показывает мощь системы загрузки Phaser 3 для работы с оптимизированными активами. Разработчик описывает все возможные варианты текстуры в одном месте, а движок сам выбирает лучший из поддерживаемых. Для экспериментов попробуйте
- Добавить свои форматы сжатия (например, проверьте поддержку
"ASTC"в разных браузерах) - Создать несколько текстур в пакете с разными ключами
- Использовать эту технику для загрузки не только текстур, но и других типов ассетов, поддерживаемых методом
pack - Вручную отключить поддержку современных кодеков в браузере для тестирования резервного PNG-варианта
