О чем этот пример
Создание множества однотипных объектов с вариативными параметрами — частая задача в разработке игр. Phaser предлагает элегантный подход через конфигурационные объекты и фабрику `this.make`. В этой статье мы разберем, как создавать десятки анимированных спрайтов с помощью всего нескольких строк кода, используя рандомизацию параметров и гибкую настройку анимаций прямо в конфиге. Это не только сокращает объем кода, но и делает его более читаемым и удобным для модификации.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('gems', 'assets/tests/columns/gems.png', 'assets/tests/columns/gems.json');
}
create ()
{
// Define the animations first
this.anims.create({ key: 'ruby', frames: this.anims.generateFrameNames('gems', { prefix: 'ruby_', end: 6, zeroPad: 4 }), repeat: -1 });
this.anims.create({ key: 'square', frames: this.anims.generateFrameNames('gems', { prefix: 'square_', end: 14, zeroPad: 4 }), repeat: -1 });
// The Sprite config
const config = {
key: 'gems',
x: { randInt: [ 0, 800 ] },
y: { randInt: [ 0, 300 ] },
scale: { randFloat: [ 0.5, 1.5 ] },
anims: 'ruby'
};
// Make 16 sprites using the config above
for (let i = 0; i < 16; i++)
{
this.make.sprite(config);
}
// A more complex animation config object.
// This time with a call to delayedPlay that's a function.
const config2 = {
key: 'gems',
frame: 'square_0000',
x: { randInt: [ 0, 800 ] },
y: { randInt: [ 300, 600 ] },
scale: { randFloat: [ 0.5, 1.5 ] },
anims: {
key: 'square',
repeat: -1,
repeatDelay: { randInt: [ 1000, 4000 ] },
delayedPlay: function ()
{
return Math.random() * 6000;
}
}
};
// Make 16 sprites using the config above
for (let i = 0; i < 16; i++)
{
this.make.sprite(config2);
}
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
pixelArt: true,
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Загрузка атласа и создание анимаций
Перед созданием спрайтов необходимо загрузить ресурсы и определить анимации. В методе preload загружается атлас gems, содержащий кадры для анимаций.
this.load.atlas('gems', 'assets/tests/columns/gems.png', 'assets/tests/columns/gems.json');
В методе create создаются две анимации: ruby и square. Метод this.anims.generateFrameNames автоматически генерирует массив кадров на основе префикса и диапазона.
this.anims.create({ key: 'ruby', frames: this.anims.generateFrameNames('gems', { prefix: 'ruby_', end: 6, zeroPad: 4 }), repeat: -1 });
this.anims.create({ key: 'square', frames: this.anims.generateFrameNames('gems', { prefix: 'square_', end: 14, zeroPad: 4 }), repeat: -1 });
Параметр zeroPad: 4 добавляет нули к номеру кадра (например, ruby_0001), что соответствует именам файлов в атласе. repeat: -1 задает бесконечное повторение анимации.
Первый конфиг: базовое создание спрайтов
Phaser позволяет описывать свойства спрайта в виде конфигурационного объекта. Это мощный инструмент для пакетного создания объектов.
const config = {
key: 'gems',
x: { randInt: [ 0, 800 ] },
y: { randInt: [ 0, 300 ] },
scale: { randFloat: [ 0.5, 1.5 ] },
anims: 'ruby'
};
* `key`: Указывает ключ текстуры или атласа.
* `x`, `y`: Используют специальный объект `{ randInt: [min, max] }` для задания случайной позиции в указанном диапазоне.
* `scale`: Аналогично, `{ randFloat: [min, max] }` задает случайный масштаб.
* `anims`: Просто строка `'ruby'` — ключ ранее созданной анимации, которая будет проигрываться сразу.
Создание 16 спрайтов по этому конфигу выполняется простым циклом:
for (let i = 0; i < 16; i++) {
this.make.sprite(config);
}
Фабрика this.make.sprite принимает конфиг и создает готовый спрайт. Каждый вызов использует свежие случайные значения для `x,yиscale`.
Второй конфиг: продвинутая настройка анимации
Второй пример демонстрирует более сложный конфиг, где настройка анимации передается не строкой, а целым объектом.
const config2 = {
key: 'gems',
frame: 'square_0000', // Стартовый кадр
x: { randInt: [ 0, 800 ] },
y: { randInt: [ 300, 600 ] },
scale: { randFloat: [ 0.5, 1.5 ] },
anims: {
key: 'square',
repeat: -1,
repeatDelay: { randInt: [ 1000, 4000 ] },
delayedPlay: function () {
return Math.random() * 6000;
}
}
};
* frame: Явно задает начальный статичный кадр (square_0000) до начала анимации.
* anims: Теперь это объект, расширяющий базовую анимацию square.
* repeatDelay: Задает случайную паузу (от 1 до 4 секунд) между повторениями анимации.
* delayedPlay: Функция, которая возвращает задержку (до 6 секунд) перед *первым* запуском анимации. Это позволяет создать эффект "разнородности" в поведении группы объектов.
Создание спрайтов происходит аналогично:
for (let i = 0; i < 16; i++) {
this.make.sprite(config2);
}
Каждый спрайт будет иметь уникальную задержку перед стартом и между циклами анимации.
Системный взгляд: как это работает
Ключевой компонент здесь — фабрика игровых объектов, доступная через `this.make`. Когда `this.make.sprite(config)` получает конфиг, она выполняет несколько шагов:
1. **Разрешение значений**: Для свойств вида `{ randInt: ... }` или `{ randFloat: ... }` фабрика вычисляет итоговое значение. Функции (как `delayedPlay`) вызываются, и их результат подставляется в итоговый конфиг анимации.
2. **Создание спрайта**: На основе итоговых значений создается экземпляр `Phaser.GameObjects.Sprite`.
3. **Применение анимации**: Если указано свойство `anims`, к спрайту применяется анимация. В случае с конфигом `config2` создается *новая* конфигурация анимации, которая передается в `sprite.play`. Это не изменяет глобальную анимацию `square`, созданную в `this.anims.create`.
Такой подход отделяет *описание* объекта от логики его *создания*, что соответствует принципам чистой архитектуры.
Что попробовать дальше
Использование конфигурационных объектов с this.make.sprite — это мощный паттерн в Phaser для создания групп объектов с вариативными свойствами. Он идеально подходит для генерации частиц, фоновых элементов, врагов или коллекций предметов. Для экспериментов попробуйте:
* Добавить в конфиг случайный угол поворота (angle или rotation).
* Использовать { randPick: [array] } для выбора случайной текстуры из списка.
* Комбинировать delayedPlay с разными repeatDelay, чтобы создать сложные, несинхронные паттерны движения у целой группы NPC.
* Динамически менять конфиг в цикле на основе индекса `i` для создания градиентов или паттернов.
