О чем этот пример
Создание группы одинаковых спрайтов — частая задача в разработке игр. Но что, если каждый спрайт должен выглядеть чуть иначе, не прибегая к загрузке множества текстур? Phaser предлагает элегантное решение: использование одного спрайтшита и случайных кадров для каждого объекта в группе. Это мощный приём для генерации разнообразных визуальных элементов, таких как толпа, звёзды на небе или коллекция предметов, без ручного размещения каждого варианта. В статье разберём, как работает `randomFrame` в `this.make.group`, и как с его помощью наполнить сцену сотнями уникальных спрайтов в несколько строк кода.
Версия 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.spritesheet('bobs', 'assets/sprites/bobs-by-cleathley.png', { frameWidth: 32, frameHeight: 32 });
}
create ()
{
// This will create a Group with 400 children.
// Each child will use the 'bobs' texture and a random frame number between 0 and 399 (inclusive)
// Change 'randomFrame' to false to see the difference it makes
const group = this.make.group({
key: 'bobs',
frame: Phaser.Utils.Array.NumberArray(0, 399),
randomFrame: true,
gridAlign: {
x: 16,
y: 16,
width: 25,
height: 25,
cellWidth: 32,
cellHeight: 32
}
});
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка: загрузка спрайтшита
Перед созданием группы необходимо загрузить текстуру. В данном примере используется спрайтшит — изображение, содержащее множество отдельных кадров (спрайтов).
this.load.spritesheet('bobs', 'assets/sprites/bobs-by-cleathley.png', { frameWidth: 32, frameHeight: 32 });
Метод load.spritesheet загружает изображение и нарезает его на кадры размером 32x32 пикселя. Ключ 'bobs' будет использоваться для доступа к этому ресурсу. Спрайтшит в примере содержит 400 последовательных кадров, что идеально подходит для демонстрации.
Создание группы с `randomFrame`
Основная логика находится в методе create. Здесь создаётся группа (Group) — специальный контейнер Phaser для управления множеством игровых объектов.
const group = this.make.group({
key: 'bobs',
frame: Phaser.Utils.Array.NumberArray(0, 399),
randomFrame: true,
gridAlign: {
x: 16,
y: 16,
width: 25,
height: 25,
cellWidth: 32,
cellHeight: 32
}
});
Рассмотрим ключевые параметры:
- key: Указывает на загруженный спрайтшит 'bobs'.
- frame: Задаёт массив номеров кадров, которые могут быть использованы. Phaser.Utils.Array.NumberArray(0, 399) создаёт массив чисел от 0 до 399 включительно.
- randomFrame: Самый важный параметр. Если установлен в true, каждый создаваемый в группе спрайт получит случайный кадр из массива frame. Если false, все спрайты будут использовать первый кадр (или кадр по умолчанию).
- gridAlign: Автоматически выравнивает спрайты в сетке 25x16 (400 объектов), начиная с координат (16, 16) и с шагом (cellWidth, cellHeight).
Как работает выбор кадра?
Когда randomFrame равен true, фаза создания каждого спрайта в группе включает дополнительный шаг: выбор случайного элемента из предоставленного массива frame. Это происходит "под капотом" метода make.group.
// Псевдокод логики внутри Phaser при randomFrame: true
for (let i = 0; i < totalObjects; i++) {
const randomFrameIndex = Phaser.Math.Between(0, frameArray.length - 1);
const spriteFrame = frameArray[randomFrameIndex];
// Создаётся спрайт с этим кадром
}
Именно это приводит к тому, что все 400 бобов на сцене выглядят по-разному, хотя используют одну текстуру. Без randomFrame (или с randomFrame: false) все спрайты получили бы кадр номер 0, создав однородную сетку.
Практическое применение и вариации
Этот приём выходит за рамки декоративных бобов. Вот несколько идей для ваших игр:
1. **Создание окружения:** Используйте спрайтшит с 10-20 вариантами травы или камней, чтобы сгенерировать натурально выглядящий ландшафт без паттернов. 2. **Толпа или армия:** Несколько типов анимации "ожидания" (idle) для юнитов в стратегической игре, чтобы они не двигались синхронно. 3. **Инвентарь или коллекции:** Для отображения стопки монет или груды самоцветов, где каждый элемент имеет небольшие визуальные отличия.
Вы можете контролировать пул кадров, изменяя массив frame.
// Только каждый пятый кадр (0, 5, 10, ...)
frame: Phaser.Utils.Array.NumberArrayStep(0, 399, 5),
// Конкретный набор кадров
frame: [0, 15, 100, 255, 399],
Изменяя gridAlign или используя setXY после создания, можно расположить объекты в любом порядке.
Что попробовать дальше
Параметр randomFrame в this.make.group — это небольшой, но мощный инструмент для добавления визуального разнообразия в вашу игру. Он позволяет создавать сложные на вид композиции из минимального количества ресурсов, разгружая художника и упрощая код. Для экспериментов попробуйте изменить логику выбора кадров: например, задать веса для разных кадров (чтобы одни встречались чаще других) или привязать выбор кадра к позиции спрайта в сетке, создавая плавные градиенты.
