Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Unity_в_действии_Джозеф_Хокинг_Рус.pdf
Скачиваний:
83
Добавлен:
21.06.2022
Размер:
26.33 Mб
Скачать

8.3. Управление инвентаризационными данными и состоянием игры      205

ВНИМАНИЕ  Коллекции находятся в собственном пространстве имен, которое следует включить в сценарий; обратите внимание на дополнительную инструкцию using, появившуюся в верхней части листинга. Не забудьте добавить ее в свои сценарии!

Так как все диспетчеры реализуют интерфейс IGameManager, код может перечислить их как данный тип и вызвать определенный в каждом из них метод Startup(). Стартовая последовательность запускается как сопрограмма, то есть асинхронно с другими частями игры (например, анимированным индикатором выполнения на заставке).

Функция запуска сначала в цикле просматривает список диспетчеров и для каждого из них вызывает метод Startup(). Затем она входит в цикл, проверяющий, загрузился ли каждый из диспетчеров, и ожидает завершения этого процесса. После этого функция запуска уведомляет нас о загрузке и завершает свою работу.

СОВЕТ  Инициализация написанных нами диспетчеров крайне проста и не связана с временем ожидания, но в общем случае эта стартовая последовательность на базе сопрограмм позволяет элегантно обрабатывать асинхронные задания запуска с долгим временем выполнения, такие как загрузка сохраненных данных.

Итак, наша структура кода готова. Вернитесь в редактор Unity и создайте пустой объект GameObject; как обычно, расположите его в точке с координатами 0,0,0 и присвойте ему значимое имя, например Game Managers. Свяжите с этим объектом сценарии

Managers, PlayerManager и InventoryManager.

В процессе воспроизведения игры вы не заметите никаких изменений, но на консоли появится ряд сообщений, информирующих о выполнении стартовой последовательности. Как видите, диспетчеры запускаются корректно, значит, пришло время заняться программированием диспетчера инвентаря.

8.3.3. Сохранение инвентаря в виде коллекции: списки и словари

Собранные персонажем элементы можно сохранить в виде объекта List. Следующий листинг добавляет такой список в диспетчер инвентаря.

Листинг 8.14. Добавление элементов в InventoryManager

...

private List<string> _items;

public void Startup() {

Debug.Log("Inventory manager starting...");

_items = new List<string>(); ¬ Инициализируем пустой список элементов.

status = ManagerStatus.Started;

}

private void DisplayItems() { ¬ Вывод на консоль сообщения о текущем инвентаре. string itemDisplay = "Items: ";

foreach (string item in _items) { itemDisplay += item + " ";

}

206      Глава 8. Добавление в игру интерактивных устройств и элементов

Debug.Log(itemDisplay);

}

public void AddItem(string name) { ¬

Другие сценарии не могут напрямую управлять списком

_items.Add(name);

элементов, но могут вызывать этот метод.

 

DisplayItems();

 

}

 

...

 

В сценарии InventoryManager появилось два важных дополнения. Во-первых, мы добавили объект List, предназначенный для хранения элементов. Во-вторых, появился открытый метод AddItem(), который может вызываться другим кодом. Эта функция добавляет элемент в список и выводит список на консоль. Теперь давайте слегка скорректируем сценарий CollectibleItem, добавив в него вызов нового метода AddItem(), как показано в следующем листинге.

Листинг 8.15. Применение нового диспетчера InventoryManager в сценарии CollectibleItem

...

void OnTriggerEnter(Collider other) { Managers.Inventory.AddItem(name); Destroy(this.gameObject);

}

...

Теперь в процессе сбора элементов персонажем вы увидите, как растет список инвентаря в выводимом на консоль сообщении. При этом всплывет недостаток такой структуры данных, как список: все собранные элементы одного типа (например, второй элемент Health) появятся в списке, как показано на рис. 8.6, хотя разумнее было бы подсчитывать количество таких элементов. Разумеется, можно себе представить вариант игры, в котором каждый элемент нужно отслеживать отдельно, но в большинстве игр подсчитывается количество копий одного элемента. Устранить этот недостаток вполне реально средствами списка, но более естественно и эффективно прибегнуть к другой структуре данных, которая называется словарем.

Рис. 8.6. Консольное сообщение, в котором однотипные элементы перечисляются несколько раз

ОПРЕДЕЛЕНИЕ  Структура данных Dictionary также взята из языка C#. Доступ к записям словаря осуществляется по идентификатору (или ключу), а не по их местоположению. Словарь напоминает хэш-таблицу, но более гибок, так как ключами могут выступать данные практически любого типа (например, «верните записи для этого объекта GameObject»).

Отредактируйте код сценария InventoryManager, заменив структуру данных List структурой Dictionary. Для этого вставьте вместо кода из листинга 8.14 код следующего листинга.

8.3. Управление инвентаризационными данными и состоянием игры      207

Листинг 8.16. Словарь элементов в InventoryManager

...

private Dictionary<string, int> _items; ¬ При объявлении словаря указывается два типа: тип ключа и тип значения.

public void Startup() {

Debug.Log("Inventory manager starting...");

_items = new Dictionary<string, int>();

status = ManagerStatus.Started;

}

private void DisplayItems() { string itemDisplay = "Items: ";

foreach (KeyValuePair<string, int> item in _items) { itemDisplay += item.Key + "(" + item.Value + ") ";

}

Debug.Log(itemDisplay);

}

public void AddItem(string name) {

if (_items.ContainsKey(name)) { ¬ Проверка существующих записей перед вводом новых данных.

_items[name] += 1;

}else { _items[name] = 1;

}

DisplayItems();

}

...

По большому счету этот код выглядит так же, как и раньше, но появилось несколько отличий. Если раньше вы не сталкивались с такими структурами данных, как Dictionary, обратите внимание, что при ее объявлении указывается два типа. Если структура List объявляется только с одним типом (типом сохраняемых в нее значений), то Dictionary объявляет как тип ключей (то есть идентификаторов, по которым будет вестись поиск), так и тип значений.

В методе AddItem() появился дополнительный алгоритм. Если раньше каждый элемент просто добавлялся в список, теперь первым делом требуется проверить наличие такого же элемента в словаре; именно этим занимается метод ContainsKey(). Если перед нами новый элемент, счет начинается с единицы, если же такой элемент уже существует, на единицу увеличивается сохраненное значение. Воспроизведите сцену, и вы убедитесь, что теперь в сообщениях о сборе инвентаря элементы одного типа объединяются, как показано на рис. 8.7.

Рис. 8.7. Консольное сообщение, в котором элементы одного типа объединяются

Наконец-то мы систематизировали в инвентаре игрока собранные элементы! Скорее всего, вам кажется, что для решения такой простой задачи мы написали слишком