О чем этот пример
Загрузка ресурсов в Phaser обычно предполагает наличие внешних файлов. Но что, если нужно быстро протестировать спрайты или встроить небольшой атлас прямо в код игры? Phaser позволяет загрузить атлас текстур, передав JSON-данные напрямую в метод `load.atlas`. Этот подход исключает лишние HTTP-запросы на этапе разработки, ускоряет итерации и полезен для создания изолированных демо или прототипов, где важно сохранить всю логику в одном файле.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
atlasJSON = {
frames: [
{
filename: '128x128',
frame: {x: 893,y: 342,w: 128,h: 128},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 128,h: 128},
sourceSize: {w: 128,h: 128},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'advanced_wars_land',
frame: {x: 132,y: 641,w: 320,h: 48},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 320,h: 48},
sourceSize: {w: 320,h: 48},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'contra2',
frame: {x: 2,y: 316,w: 142,h: 222},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 142,h: 222},
sourceSize: {w: 142,h: 222},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'contra3',
frame: {x: 645,y: 197,w: 246,h: 201},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 246,h: 201},
sourceSize: {w: 246,h: 201},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'diamonds32x5',
frame: {x: 585,y: 596,w: 318,h: 49},
rotated: false,
trimmed: true,
spriteSourceSize: {x: 1,y: 15,w: 318,h: 49},
sourceSize: {w: 320,h: 64},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'exocet_spaceman',
frame: {x: 146,y: 316,w: 153,h: 175},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 153,h: 175},
sourceSize: {w: 153,h: 175},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'explosion',
frame: {x: 2,y: 2,w: 319,h: 312},
rotated: false,
trimmed: true,
spriteSourceSize: {x: 1,y: 6,w: 319,h: 312},
sourceSize: {w: 320,h: 320},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'helix',
frame: {x: 724,y: 472,w: 221,h: 28},
rotated: false,
trimmed: true,
spriteSourceSize: {x: 6,y: 0,w: 221,h: 28},
sourceSize: {w: 233,h: 30},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'knight3',
frame: {x: 323,y: 204,w: 320,h: 131},
rotated: false,
trimmed: true,
spriteSourceSize: {x: 0,y: 0,w: 320,h: 131},
sourceSize: {w: 320,h: 200},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'lance-overdose-loader-eye',
frame: {x: 2,y: 540,w: 128,h: 128},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 128,h: 128},
sourceSize: {w: 128,h: 128},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'mask-test',
frame: {x: 323,y: 2,w: 320,h: 200},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 320,h: 200},
sourceSize: {w: 320,h: 200},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'metalslug_monster39x40',
frame: {x: 436,y: 337,w: 156,h: 160},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 156,h: 160},
sourceSize: {w: 156,h: 160},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'pacman_by_oz_28x28',
frame: {x: 454,y: 647,w: 308,h: 28},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 308,h: 28},
sourceSize: {w: 308,h: 28},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'parsec',
frame: {x: 281,y: 527,w: 302,h: 80},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 302,h: 80},
sourceSize: {w: 302,h: 80},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'profil-sad-plush',
frame: {x: 146,y: 493,w: 133,h: 142},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 133,h: 142},
sourceSize: {w: 133,h: 142},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'saw',
frame: {x: 594,y: 400,w: 128,h: 128},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 128,h: 128},
sourceSize: {w: 128,h: 128},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'shocktroopers-lulu2',
frame: {x: 301,y: 337,w: 133,h: 188},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 133,h: 188},
sourceSize: {w: 133,h: 188},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'snowflakes_large',
frame: {x: 585,y: 530,w: 379,h: 64},
rotated: false,
trimmed: true,
spriteSourceSize: {x: 2,y: 0,w: 379,h: 64},
sourceSize: {w: 384,h: 64},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'spaceman',
frame: {x: 724,y: 502,w: 225,h: 16},
rotated: false,
trimmed: true,
spriteSourceSize: {x: 15,y: 0,w: 225,h: 16},
sourceSize: {w: 240,h: 16},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'steelpp-font',
frame: {x: 645,y: 2,w: 320,h: 193},
rotated: false,
trimmed: true,
spriteSourceSize: {x: 0,y: 0,w: 320,h: 193},
sourceSize: {w: 320,h: 200},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'treasure_trap',
frame: {x: 893,y: 197,w: 127,h: 143},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 127,h: 143},
sourceSize: {w: 127,h: 143},
pivot: {x: 0.5,y: 0.5}
},
{
filename: 'vu',
frame: {x: 281,y: 609,w: 300,h: 30},
rotated: false,
trimmed: false,
spriteSourceSize: {x: 0,y: 0,w: 300,h: 30},
sourceSize: {w: 300,h: 30},
pivot: {x: 0.5,y: 0.5}
} ],
meta: {
app: 'http://www.codeandweb.com/texturepacker',
version: '1.0',
image: 'megaset-3.png',
format: 'RGBA8888',
size: {w: 1023,h: 691},
scale: '1',
smartupdate: '$TexturePacker:SmartUpdate:5e8f90752cfd57d3adfb39bcd3eef1b6:87d98cec6fa616080f731b87726d6a1e:b55588eba103b49b35a0a59665ed84fd#39;
}
};
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('megaset', 'assets/atlas/megaset-3.png', this.atlasJSON);
}
create ()
{
const atlasTexture = this.textures.get('megaset');
const frames = atlasTexture.getFrameNames();
for (let i = 0; i < frames.length; i++)
{
const x = Phaser.Math.Between(0, 800);
const y = Phaser.Math.Between(0, 600);
this.add.image(x, y, 'megaset', frames[i]);
}
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Хранение данных атласа в классе сцены
Вместо загрузки внешнего JSON-файла, структура атласа определяется прямо в коде как свойство класса сцены. Это обычный JavaScript-объект, соответствующий формату Texture Packer JSON Hash. Он содержит массивы frames и объект meta с метаинформацией.
Объект atlasJSON включает всю необходимую информацию о каждом кадре (спрайте): его имя, координаты и размеры в большом изображении, данные об обрезке и точке поворота. Это даёт полный контроль над данными атласа.
Загрузка атласа с локальными JSON-данными
Загрузка происходит в методе preload(). Ключевой момент — третий аргумент метода this.load.atlas. В него передаётся наш локальный объект this.atlasJSON, вместо пути к JSON-файлу.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('megaset', 'assets/atlas/megaset-3.png', this.atlasJSON);
}
Phaser использует переданный объект для парсинга атласа. Обратите внимание, что само изображение (megaset-3.png) загружается с удалённого URL, установленного через setBaseURL. Этот метод гибридный: изображение грузится из сети, а данные о его структуре — из кода.
Получение кадров и создание спрайтов
После загрузки в методе create() мы получаем доступ к текстуре по её ключу 'megaset'. Из текстуры можно извлечь имена всех кадров (спрайтов), которые были описаны в JSON.
const atlasTexture = this.textures.get('megaset');
const frames = atlasTexture.getFrameNames();
Метод getFrameNames() возвращает массив строк — имён файлов, указанных в frames[].filename. Это имена спрайтов, которые можно использовать для создания изображений.
Случайное размещение спрайтов на сцене
Получив список имён кадров, мы в цикле создаём изображения (this.add.image), используя ключ атласа и имя конкретного кадра.
for (let i = 0; i < frames.length; i++)
{
const x = Phaser.Math.Between(0, 800);
const y = Phaser.Math.Between(0, 600);
this.add.image(x, y, 'megaset', frames[i]);
}
Для каждого спрайта генерируются случайные координаты в пределах сцены с помощью Phaser.Math.Between. Это наглядно демонстрирует, что все спрайты из атласа загружены корректно и готовы к использованию.
Что попробовать дальше
Использование локального JSON-объекта для загрузки атласа — отличный приём для быстрого прототипирования и создания самодостаточных примеров кода. Он избавляет от необходимости управлять дополнительными файлами на ранних этапах. Поэкспериментируйте с этим подходом: попробуйте динамически генерировать объект atlasJSON для тайловых карт, встраивать небольшие UI-атласы прямо в код бандла или комбинировать этот метод с загрузкой изображения из Base64 строки, чтобы получить полностью автономный ресурс.
