О чем этот пример
При работе с Texture Atlas в Phaser каждый фрейм — это не просто прямоугольник с картинкой. В него можно загрузить пользовательские данные, которые затем использовать в игре. В этом примере показано, как хранить предустановленный цвет тинтинга (`tint`) прямо в JSON-описании атласа и применять его к спрайтам при создании. Этот подход позволяет дизайнерам задавать визуальные параметры прямо в данных атласа, делая контент более самодостаточным и удобным для итераций.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
atlasJSON = {
frames: [
{
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},
tint: 0xff0000
},
{
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},
tint: 0x00ff00
},
{
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},
tint: 0xff00ff
},
{
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},
tint: 0x0000ff
} ],
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();
let x = 100;
for (let i = 0; i < frames.length; i++)
{
const img = this.add.image(x, 300, 'megaset', frames[i]);
x += 200;
// The frames have custom data stored in them (see the JSON below) which we can use to tint them:
img.setTint(img.frame.customData.tint);
}
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Структура кастомных данных в атласе
Ключевая идея в том, что объект, описывающий фрейм в JSON-атласе, может содержать любые пользовательские свойства помимо стандартных (frame, rotated и т.д.). Phaser загружает эти свойства и делает их доступными.
В предоставленном примере для каждого фрейма добавлено свойство tint с шестнадцатеричным значением цвета (например, 0xff0000 для красного). Это свойство не является частью стандартного формата Texture Packer — оно добавлено разработчиком вручную. После загрузки атласа Phaser сохраняет весь объект фрейма, включая это кастомное свойство, в текстуру.
{
filename: 'contra2',
frame: {x: 2, y: 316, w: 142, h: 222},
// ... другие стандартные свойства ...
pivot: {x: 0.5, y: 0.5},
tint: 0xff0000 // Пользовательское свойство
}
Загрузка атласа с пользовательским JSON
Для загрузки используется метод this.load.atlas. Важный момент: вместо указания пути к JSON-файлу, мы передаём сам JavaScript-объект с данными атласа (this.atlasJSON). Это позволяет динамически генерировать или модифицировать данные атласа перед загрузкой, что открывает возможности для процедурной генерации.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('megaset', 'assets/atlas/megaset-3.png', this.atlasJSON);
}
После выполнения этого кода в кэше текстур появится текстура с ключом 'megaset', содержащая все фреймы и их пользовательские данные.
Доступ к фреймам и их пользовательским данным
В методе create мы получаем ссылку на загруженную текстуру атласа. Метод getFrameNames() возвращает массив имён всех фреймов. Далее в цикле создаются изображения, и для каждого из них извлекается кастомное свойство tint.
create ()
{
const atlasTexture = this.textures.get('megaset');
const frames = atlasTexture.getFrameNames();
let x = 100;
for (let i = 0; i < frames.length; i++)
{
const img = this.add.image(x, 300, 'megaset', frames[i]);
x += 200;
// Доступ к пользовательским данным фрейма
img.setTint(img.frame.customData.tint);
}
}
Обратите внимание на путь: img.frame.customData.tint. Свойство frame объекта Image ссылается на объект фрейма в текстуре. Все нестандартные свойства исходного JSON попадают в объект customData этого фрейма. Таким образом, мы можем прочитать заранее заданный цвет и применить его методом setTint.
Практическое применение и расширение идеи
Типтинг — лишь простой пример. В свойстве customData можно хранить любую информацию, полезную для игровой логики:
* **Смещение коллайдера:** `{ colliderOffset: { x: 0, y: 10 } }`.
* **Сила атаки или тип урона для спрайта оружия:** `{ attackPower: 15, damageType: 'fire' }`.
* **Имя анимации для автоматического проигрывания:** `{ idleAnim: 'hero_idle' }`.
* **Звуковые эффекты:** `{ sfxKey: 'jump_sound' }`.
Это создаёт мощную связь между контентом (данными атласа, которые может готовить дизайнер) и кодом. Изменения в балансе или визуале можно вносить, правя JSON, без обязательного изменения исходного кода игры.
// Пример чтения других кастомных данных
if (img.frame.customData.attackPower) {
this.player.damage = img.frame.customData.attackPower;
}
Что попробовать дальше
Использование customData во фреймах атласа — это элегантный паттерн для хранения метаданных, специфичных для вашей игры. Он уменьшает жесткую связь между артом и кодом. Для экспериментов попробуйте
- Добавить в JSON свойство
scaleи автоматически масштабировать спрайты при создании - Хранить вероятность выпадения предмета для спрайтов лута
- Динамически модифицировать объект
atlasJSONвpreload, генерируя данные (например, случайные цвета) на лету
