О чем этот пример
В процессе разработки игры часто возникает необходимость привязать к игровому объекту дополнительную информацию: стоимость предмета, уровень защиты, имя персонажа и так далее. Использование обычных свойств объекта быстро приводит к хаосу. Phaser предоставляет встроенную и эффективную систему `DataManager`, которая позволяет хранить произвольные данные и, что самое главное, удобно их запрашивать. Эта статья покажет, как использовать эту систему для создания чистого и масштабируемого кода.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('gem', 'assets/sprites/gem.png');
}
create ()
{
const image = this.add.image(this.game.config.width / 2, this.game.config.height / 2, 'gem');
image.setDataEnabled();
image.data.set('name', 'Red GemStone');
image.data.set('value_armor', true);
image.data.set('armor_head', 50);
image.data.set('armor_body', 250);
image.data.set('armor_feet', 15);
// Query lets you run a regular expression against the keys,
// and get an object back with them all in.
// Here we're checking for keys matching 'armor' at the start
console.log(image.data.query(/^armor/));
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое DataManager?
DataManager — это компонент, который можно подключить к любому игровому объекту Phaser (спрайту, изображению, тексту). Он предоставляет структурированное хранилище для произвольных данных по принципу ключ-значение.
Основные преимущества: * **Структурированность:** Данные хранятся отдельно от основных свойств объекта, что делает код чище. * **Безопасность:** Исключаются конфликты имён со стандартными свойствами Phaser. * **Мощный поиск:** Ключи можно искать с помощью регулярных выражений, что идеально подходит для группировки данных.
Чтобы начать работу, объекту необходимо включить эту систему.
Включение системы данных и запись значений
Перед записью данных нужно активировать DataManager для конкретного объекта с помощью метода setDataEnabled().
После этого для записи данных используется метод .data.set(). Первым аргументом передаётся ключ (идентификатор), вторым — значение. Значением может быть что угодно: строка, число, булево значение, объект или массив.
Давайте создадим изображение и сохраним для него несколько свойств.
const image = this.add.image(this.game.config.width / 2, this.game.config.height / 2, 'gem');
// Включаем систему данных для этого объекта
image.setDataEnabled();
// Записываем данные
image.data.set('name', 'Red GemStone');
image.data.set('value_armor', true);
image.data.set('armor_head', 50);
image.data.set('armor_body', 250);
image.data.set('armor_feet', 15);
Теперь к объекту image привязаны данные, которые можно извлекать по отдельности с помощью image.data.get('armor_head').
Мощный поиск данных с помощью .query()
Самая сильная сторона DataManager — это возможность выборки данных по шаблону. Метод query() принимает регулярное выражение, которому должны соответствовать ключи, и возвращает объект со всеми найденными парами ключ-значение.
Это невероятно полезно, когда у вас есть группы связанных данных, например, все свойства, начинающиеся с "armor_".
Посмотрим, как это работает в нашем примере:
// Ищем все ключи, которые начинаются со строки 'armor'
console.log(image.data.query(/^armor/));
В консоли появится объект:
{
armor_head: 50,
armor_body: 250,
armor_feet: 15
}
Регулярное выражение /^armor/ означает "начало строки, за которым следует 'armor'". Таким образом, ключ value_armor найден не будет, так как ему предшествует value_.
Практическое применение в играх
Давайте рассмотрим несколько реальных сценариев, где эта система незаменима.
**1. Инвентарь и предметы:**
// Создаём зелье
const potion = this.physics.add.sprite(x, y, 'potion');
potion.setDataEnabled();
potion.data.set('type', 'healing');
potion.data.set('power', 25);
potion.data.set('price', 100);
// Позже, при подборе, мы можем проверить все предметы с типом 'healing' в инвентаре.
**2. Характеристики врагов:**
// Создаём врага и задаём его параметры
enemy.data.set('stats_health', 200);
enemy.data.set('stats_damage', 15);
enemy.data.set('reward_exp', 50);
enemy.data.set('reward_gold', 10);
// При получении урона можно легко получить все 'stats_' значения для отображения в UI.
**3. Квестовые метки:**
// Помечаем NPC как дающего определённый квест
npc.data.set('quest_giver', true);
npc.data.set('quest_id', 'find_gem');
npc.data.set('dialogue_start', 'Привет, ищешь сокровища?');
Что попробовать дальше
Встроенная система данных Phaser — это мощный инструмент для организации связанной с объектами информации, который часто остаётся незамеченным. Она избавляет от необходимости создавать отдельные классы-контейнеры для простых данных и предоставляет элегантный API для их извлечения.
**Идеи для экспериментов:**
1. Создайте менеджер экипировки, который суммирует все значения armor_* с надетых предметов.
2. Реализуйте систему модификаторов (buff_*), где каждый ключ — уникальный бафф, а значение — его сила. Методом query(/^buff/) можно получить все активные эффекты.
3. Используйте query() для сохранения игры: соберите все данные объектов в сцене, отфильтровав только нужные ключи, и запишите их в localStorage.
