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

11.2. Разработка общей игровой структуры      283

добавим к обеим кнопкам подписчика на событие OnClick. Свяжите эти события со всплывающим окном и выберите подходящий вариант — OnEquip() или OnUse().

Наконец, добавим ко всем четырем изображениям элементов компонент EventTrigger. Сценарий InventoryPopup редактирует этот компонент для каждого значка, поэтому лучше, чтобы во всех случаях он присутствовал! Компонент EventTrigger находится в разделе Event (напомню, что для добавления нужно щелкнуть на кнопке Add Component). Возможно, вам будет удобнее добавить его методом копирования. Для этого нужно щелкнуть на маленькой кнопке с изображением шестерни справа от имени компонента, выбрать в появившемся меню команду Copy Component, выделить объект, в который его нужно скопировать, и воспользоваться командой Paste As New. Выполните эту операцию, но не назначайте подписчиков события, так как это уже сделано в коде сценария InventoryPopup.

На этом работа над UI для отображения инвентаря завершается! Запустите игру, чтобы посмотреть, что происходит со всплывающим окном, когда вы собираете инвентарь и щелкаете на кнопках. Мы закончили сборку фрагментов предыдущих проектов; теперь я объясню, как создать более масштабную игру с нуля.

11.2. Разработка общей игровой структуры

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

Для этого нам потребуется еще сильнее уменьшить связь сцены с отвечающими за ее обработку диспетчерами, в результате придется рассылать сообщения об их состояниях (точно так же, как диспетчер PlayerManager рассылает информацию об обновлениях состояния здоровья персонажа). Создайте новый сценарий StartupEvent (листинг 11.15); происходящее при загрузке мы выделяем в отдельный сценарий, так как эти события идут в связке с постоянно использующейся системой диспетчеров, в то время как класс GameEvent связан с конкретной игрой.

Листинг 11.15. Сценарий StartupEvent

public static class StartupEvent {

public const string MANAGERS_STARTED = "MANAGERS_STARTED"; public const string MANAGERS_PROGRESS = "MANAGERS_PROGRESS";

}

Теперь можно приступать к редактированию диспетчеров, добавляя к ним средства рассылки сообщений о новых событиях!

11.2.1. Управление ходом миссии и набором уровней

Пока что проект состоит из одной сцены, в которой в числе прочего находится главный диспетчер. Проблема в том, что у каждой сцены будет собственный набор игровых диспетчеров, в то время как нам требуется один набор, доступный всем сценам.

284      Глава 11. Объединение фрагментов в готовую игру

Для этого мы создадим отдельную сцену Startup, которая будет инициализировать диспетчеры, а затем давать к ним доступ остальным игровым сценам.

Также нам нужен новый диспетчер, обрабатывающий процесс прохождения игры. Создайте сценарий MissionManager и скопируйте в него содержимое следующего листинга.

Листинг 11.16. Сценарий MissionManager

using UnityEngine;

using System.Collections;

using System.Collections.Generic;

public class MissionManager : MonoBehaviour, IGameManager { public ManagerStatus status {get; private set;}

public int curLevel {get; private set;} public int maxLevel {get; private set;}

private NetworkService _network;

public void Startup(NetworkService service) { Debug.Log("Mission manager starting...");

_network = service;

curLevel = 0; maxLevel = 1;

status = ManagerStatus.Started;

}

public void GoToNext() {

if (curLevel < maxLevel) { ¬ Рассылаем аргументы вместе с объектом WWW, используя объект WWWForm. curLevel++;

string name = "Level" + curLevel; Debug.Log("Loading " + name);

Application.LoadLevel(name); ¬ Проверяем, достигнут ли последний уровень.

}else {

Debug.Log("Last level");

}

}

}

По большей части ничего необычного в этом листинге не происходит. Обратить внимание имеет смысл на расположенный в конце метод LoadLevel(); я уже упоминал про него в главе 5, но до текущего момента он нас не особо интересовал. Этот метод служит в Unity для загрузки файла сцены; в главе 5 он использовался для перезагрузки одной и той же сцены, теперь же мы с его помощью начнем загружать произвольные сцены, передавая имена соответствующих файлов.

Свяжите этот сценарий с объектом Game Managers. Заодно добавьте к сценарию Managers новый компонент (см. следующий листинг).

11.2. Разработка общей игровой структуры      285

Листинг 11.17. Добавление нового компонента в сценарий Managers

...

[RequireComponent(typeof(MissionManager))]

public class Managers : MonoBehaviour {

public static PlayerManager Player {get; private set;} public static InventoryManager Inventory {get; private set;} public static MissionManager Mission {get; private set;}

...

void Awake() {

DontDestroyOnLoad(gameObject); ¬ Команда Unity для сохранения объекта между сценами.

Player = GetComponent<PlayerManager>();

Inventory = GetComponent<InventoryManager>();

Mission = GetComponent<MissionManager>();

_startSequence = new List<IGameManager>(); _startSequence.Add(Player); _startSequence.Add(Inventory); _startSequence.Add(Mission);

StartCoroutine(StartupManagers());

}

 

 

private IEnumerator StartupManagers() {

 

 

...

 

 

if (numReady > lastReady) {

 

 

Debug.Log("Progress: " + numReady + "/" + numModules);

 

Messenger<int, int>.Broadcast(

 

Событие загрузки рассылается

StartupEvent.MANAGERS_PROGRESS, numReady, numModules); ¬

}

 

без параметров.

 

 

yield return null;

 

 

}

 

 

Debug.Log("All managers started up");

Событие загрузки рассылается вместе

Messenger.Broadcast(StartupEvent.MANAGERS_STARTED); ¬

с относящимися к нему данными.

}

...

Большая часть этого кода вам уже знакома (процесс добавления диспетчера MissionManager ничем не отличается от процесса добавления всех прочих диспетчеров), но появились и два новых фрагмента. Во-первых, событие, рассылаемое с двумя целочисленными значениями. Раньше вы видели обобщенные события без значений и сообщения с одним числом, но данный синтаксис позволяет рассылать произвольное количество численных значений.

Вторым новым фрагментом кода является метод DontDestroyOnLoad(). В Unity он обеспечивает сохранение объекта между сценами. Обычно при загрузке новой сцены все объекты удаляются, но метод DontDestroyOnLoad() позволяет перенести объект в следующую сцену.

286      Глава 11. Объединение фрагментов в готовую игру

Разделение сцен для загрузки и игрового уровня

Так как объект Game Managers существует во всех сценах, следует разделить диспетчеры и игровые уровни. На вкладке Project создайте копию файла сцены (Edit Duplicate) и присвойте двум файлам соответствующие имена: Startup и Level1. Откройте файл Level1 и удалите объект Game Managers (он будет предоставляться сценой Startup). Откройте сцену Startup и удалите все, кроме объектов Game Managers, Controller, HUD Canvas и EventSystem. Удалите сценарные компоненты у объекта Controller, а также объекты UI (метку health и объект InventoryPopup), которые являются потомками объекта

Canvas.

Теперь у нас совершенно пустой UI, поэтому создайте новый ползунок, как показано на рис. 11.7, и сбросьте флажок Interactable. У объекта Controller теперь тоже отсутствуют компоненты сценария, поэтому создайте новый сценарий StartupController и присоедините его к объекту Controller (см. следующий листинг).

О а : Game Managers Controller

HUD Canvas EventSystem

Н -а € ‚ ƒ„…

Рис. 11.7. Сцена Startup после удаления всего ненужного

Листинг 11.18. Сценарий StartupController

using UnityEngine; using UnityEngine.UI;

using System.Collections;

public class StartupController : MonoBehaviour { [SerializeField] private Slider progressBar;

void Awake() {

Messenger<int, int>.AddListener(StartupEvent.MANAGERS_PROGRESS, OnManagersProgress);

Messenger.AddListener(StartupEvent.MANAGERS_STARTED, OnManagersStarted);

}

void OnDestroy() {

Messenger<int, int>.RemoveListener(StartupEvent.MANAGERS_PROGRESS, OnManagersProgress);

Messenger.RemoveListener(StartupEvent.MANAGERS_STARTED, OnManagersStarted);

}

private void OnManagersProgress(int numReady, int numModules) {