О чем этот пример

В процессе разработки игры часто возникает необходимость привязать к игровому объекту дополнительную информацию: стоимость предмета, уровень защиты, имя персонажа и так далее. Использование обычных свойств объекта быстро приводит к хаосу. 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.