В 2D-играх спрайтовая анимация традиционно строится на последовательной смене кадров. В библиотеке Phaser одним из наиболее эффективных способов организации кадров является использование атласа текстур (Texture Atlas). Атлас объединяет множество изображений в один графический файл и сопровождается JSON-описанием, содержащим координаты, размеры и ключи каждого кадра.
Использование атласа решает сразу несколько задач:
В контексте Phaser анимации могут создаваться не только по
нумерованной последовательности (run_01,
run_02, …), но и по ключам атласа, что
особенно важно при работе с инструментами упаковки текстур
(TexturePacker, Aseprite, CodeAndWeb и др.).
Перед созданием анимации необходимо загрузить атлас в сцене. Phaser поддерживает несколько форматов:
atlas — JSON в формате TexturePacker;atlasJSONHash — JSON с объектной структурой
кадров;atlasXML — XML-описание.Пример загрузки JSON-атласа:
preload() {
this.load.atlas('player', 'assets/player.png', 'assets/player.json');
}
Параметры:
'player' — ключ текстуры;После загрузки каждый кадр становится доступным по имени, указанному в JSON.
Типичный JSON-файл атласа содержит объект frames, внутри
которого находятся записи вида:
{
"run_01": { ... },
"run_02": { ... },
"jump": { ... }
}
Ключ "run_01" — это идентификатор кадра, который
используется при создании анимации. Phaser не требует строгой нумерации;
допустимы произвольные строки.
Это открывает возможность формировать анимации:
generateFrameNamesЕсли кадры имеют последовательные имена (run_01,
run_02, run_03), применяется генератор:
this.anims.create({
key: 'run',
frames: this.anims.generateFrameNames('player', {
prefix: 'run_',
start: 1,
end: 6,
zeroPad: 2
}),
frameRate: 12,
repeat: -1
});
Ключевые параметры:
prefix — строка перед номером;start, end — диапазон;zeroPad — количество ведущих нулей;'player' — ключ загруженного атласа.Phaser автоматически сформирует массив:
run_01
run_02
run_03
...
generateFrameNames с массивомЕсли имена не образуют строгий диапазон:
this.anims.create({
key: 'jump',
frames: this.anims.generateFrameNames('player', {
frames: ['jump_start', 'jump_air', 'jump_end']
}),
frameRate: 10,
repeat: 0
});
В данном случае список кадров задаётся вручную.
Анимацию можно сформировать без генератора:
this.anims.create({
key: 'attack',
frames: [
{ key: 'player', frame: 'attack_1' },
{ key: 'player', frame: 'attack_2' },
{ key: 'player', frame: 'attack_3' }
],
frameRate: 8,
repeat: 0
});
Такой способ удобен при смешении кадров из разных атласов или при нестандартной последовательности.
Объект конфигурации anims.create содержит важные
свойства:
| Свойство | Назначение |
|---|---|
key |
Уникальный идентификатор анимации |
frames |
Массив кадров |
frameRate |
Количество кадров в секунду |
duration |
Общая длительность (альтернатива frameRate) |
repeat |
Повтор (-1 — бесконечно) |
yoyo |
Воспроизведение в обратном направлении |
delay |
Задержка перед стартом |
repeatDelay |
Пауза между повторами |
При указании duration Phaser вычисляет
frameRate автоматически.
В Phaser анимации по умолчанию создаются глобально через
this.anims.create(). Они доступны всем объектам сцен.
Однако существует возможность создания локальной анимации непосредственно у спрайта:
sprite.anims.create({
key: 'blink',
frames: ...
});
Глобальный подход предпочтителен при многократном использовании одних и тех же последовательностей.
После создания анимации она запускается следующим образом:
sprite.play('run');
Дополнительные варианты:
sprite.play('run', true); // игнорировать повторный вызов
sprite.anims.stop();
sprite.anims.pause();
sprite.anims.resume();
Phaser автоматически переключает текстуру спрайта в соответствии с текущим кадром атласа.
Система анимаций поддерживает события:
animationstartanimationupdateanimationrepeatanimationcompleteПример обработки:
sprite.on('animationcomplete', function (animation, frame) {
if (animation.key === 'attack') {
sprite.play('idle');
}
});
События позволяют управлять логикой переходов между состояниями персонажа.
Рекомендуется придерживаться схемы:
action_direction_XX
Например:
run_left_01
run_left_02
run_right_01
Это позволяет формировать анимации динамически:
createRunAnimation(direction) {
this.anims.create({
key: `run_${direction}`,
frames: this.anims.generateFrameNames('player', {
prefix: `run_${direction}_`,
start: 1,
end: 6,
zeroPad: 2
}),
frameRate: 12,
repeat: -1
});
}
Не рекомендуется создавать анимации повторно при каждом входе в сцену. Перед созданием полезно проверять наличие:
if (!this.anims.exists('run')) {
this.anims.create({...});
}
Это предотвращает утечки памяти и дублирование данных.
При разделении персонажей по атласам:
this.load.atlas('enemy', 'enemy.png', 'enemy.json');
Создание анимации:
this.anims.create({
key: 'enemy_walk',
frames: this.anims.generateFrameNames('enemy', {
prefix: 'walk_',
start: 1,
end: 4
}),
frameRate: 6,
repeat: -1
});
Каждый атлас имеет собственный набор ключей кадров.
Инструменты упаковки часто:
Phaser корректно учитывает эти параметры, если они присутствуют в JSON. Это особенно важно для анимаций с вращением и физикой.
Возможна генерация анимаций во время выполнения игры, например при загрузке пользовательских модов или скинов.
Пример динамического списка:
const frameNames = texture.getFrameNames()
.filter(name => name.startsWith('effect_'));
this.anims.create({
key: 'effect',
frames: frameNames.map(name => ({
key: 'effectsAtlas',
frame: name
})),
frameRate: 15,
repeat: 0
});
Метод getFrameNames() позволяет анализировать содержимое
атласа.
| Подход | Преимущества | Недостатки |
|---|---|---|
| Отдельные PNG | Простота | Много запросов |
| Атлас | Производительность, компактность | Требуется сборка |
В реальных проектах атласы практически обязательны.
Типичная архитектура:
Переходы управляются через конечный автомат состояний (FSM). Анимации по ключам атласа позволяют гибко переключать визуальные состояния без смены текстуры.
Каждый кадр может иметь собственную длительность:
frames: [
{ key: 'player', frame: 'attack_1', duration: 100 },
{ key: 'player', frame: 'attack_2', duration: 50 },
{ key: 'player', frame: 'attack_3', duration: 200 }
]
Это позволяет:
Phaser использует внутренний таймер сцены. Скорость можно изменять глобально:
this.anims.globalTimeScale = 0.5;
Или локально:
sprite.anims.timeScale = 2;
Это полезно для эффектов замедления времени (slow motion).
Удаление анимации:
this.anims.remove('run');
Полная очистка:
this.anims.clear();
Важно учитывать, что удаление глобальной анимации влияет на все объекты, которые её используют.
Оптимальный подход:
Анимации по ключам атласа являются центральным элементом построения производительных 2D-игр на Phaser, обеспечивая масштабируемость, управляемость и высокую эффективность отрисовки.