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

Разработка игр — это управление состоянием и конфигурацией. Часто код обращается к глубоко вложенным свойствам объектов, что грозит ошибками `Cannot read property 'x' of undefined`. Встроенный хелпер Phaser `GetValue` позволяет безопасно извлекать значения по строковому пути, задавая значение по умолчанию. Эта статья покажет, как использовать этот инструмент для написания чистого и надёжного кода, избегая многословных проверок.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scale: {
        width: 400,
        height: 300
    },
    scene: {
        create: create
    }
};

var game = new Phaser.Game(config);

function create ()
{
    var srcA = {
        a: {
            c: 100
        }
    }

    var srcB = {
        b: {

        }
    }

    var value = Phaser.Utils.Objects.GetValue(srcA, 'a.b.c', 66, srcB)

    console.log(value)

    this.add.text(200, 200, 'hello: ' + value);
}

Проблема: обращение к несуществующим свойствам

Представьте, что в игре есть конфигурационный объект для персонажа, который может быть неполным или изменяться динамически. Прямой доступ к свойству, например, config.abilities.attack.damage, вызовет ошибку, если на любом уровне вложенности (abilities, attack) встретится undefined.

Традиционное решение — серия проверок, которая загромождает код:

let damage = 66; // Значение по умолчанию
if (config && config.abilities && config.abilities.attack) {
    damage = config.abilities.attack.damage;
}

Phaser предлагает более элегантный способ с помощью Phaser.Utils.Objects.GetValue.

Синтаксис и параметры GetValue

Метод Phaser.Utils.Objects.GetValue принимает четыре аргумента и возвращает значение свойства или значение по умолчанию.

// Сигнатура метода
var result = Phaser.Utils.Objects.GetValue(source, key, defaultValue, altSource);

* source (объект): Первичный объект для поиска. * key (строка): Путь к свойству в формате 'a.b.c'. * defaultValue (любой): Значение, которое вернётся, если свойство не найдено. * altSource (объект, опционально): Альтернативный объект, в котором будет произведён поиск, если в source свойство не найдено.

Метод проходит по цепочке свойств, указанной в key. Если на каком-то шаге встречается undefined, поиск прекращается и возвращается defaultValue.

Разбор примера из исходного кода

Давайте шаг за шагом разберём исходный пример, чтобы понять логику работы.

var srcA = {
    a: {
        c: 100
    }
};

var srcB = {
    b: {
        // Пустой объект
    }
};

var value = Phaser.Utils.Objects.GetValue(srcA, 'a.b.c', 66, srcB);
console.log(value); // Выведет: 66

1. Поиск начинается в объекте srcA по пути 'a.b.c'. 2. Находится свойство srcA.a (объект { c: 100 }). 3. Далее ищется свойство `bвнутриsrcA.a. Его **нет** (там только свойствоc). ЗначениеsrcA.a.bundefined`. 4. Поскольку поиск в srcA не увенчался успехом, метод переключается на альтернативный источник — объект srcB. 5. В srcB поиск идёт с начала пути 'a.b.c'. Свойства srcB.a не существует, оно равно undefined. 6. Так как свойство не найдено ни в srcA, ни в srcB, метод возвращает значение по умолчанию — 66.

Ключевой вывод: altSource — это не «продолжение» пути из srcA, а **полностью альтернативный объект** для поиска по тому же самому пути с начала.

Практическое применение в игровых сценах

Вот как можно использовать GetValue для настройки игровых объектов, например, спрайтов с опциональными свойствами.

function create() {
    // Конфиг врага. Свойство 'projectile.speed' отсутствует.
    var enemyConfig = {
        texture: 'orc',
        health: 50,
        projectile: {
            texture: 'fireball'
        }
    };

    // Безопасное извлечение скорости снаряда с значением по умолчанию.
    var projectileSpeed = Phaser.Utils.Objects.GetValue(enemyConfig, 'projectile.speed', 200);

    // Извлечение текстуры врага. Путь существует.
    var enemyTexture = Phaser.Utils.Objects.GetValue(enemyConfig, 'texture', 'default_enemy');

    this.add.text(10, 10, `Скорость снаряда: ${projectileSpeed}`, { fill: '#0f0' });
    this.add.sprite(100, 300, enemyTexture);
}

Этот подход идеален для: * Загрузки уровней из JSON-файлов, где некоторые параметры могут быть опущены. * Создания систем диалогов или событий с ветвлением. * Настройки физических свойств объектов, где часть параметров общая, а часть — уникальна.

Что попробовать дальше

Phaser.Utils.Objects.GetValue — это мощный инструмент для работы с конфигурацией, который делает код безопаснее и чище, избавляя от каскада условных операторов. Он особенно полезен при загрузке внешних данных или работе с большими иерархическими объектами состояния игры. **Идеи для экспериментов:** 1. Создайте систему настроек игры, где пользователь может менять только часть параметров, а остальные берутся из конфига по умолчанию. 2. Реализуйте диалоговую систему, где ответ NPC зависит от цепочки флагов player.quests.forest.elderSpoke, безопасно извлекаемой через GetValue. 3. Используйте altSource для создания системы наследования свойств: сначала ищите в конфиге конкретного монстра, а если свойства нет — в конфиге его типа (например, 'goblin').