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

78      Глава 3. Добавляем в игру врагов и снаряды

СТРУКТУРА КОДА ДЛЯ AI

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

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

Точная структура кода для AI зависит от особенностей конкретной игры; единственного «правильного» варианта в данном случае не существует. Средства Unity позволят вам легко спроектировать гибкую архитектуру.

3.4. Увеличение количества врагов

В настоящее время в сцене присутствует всего один враг, и после его смерти сцена становится пустой. Давайте заставим игру порождать врагов, сделав так, чтобы после смерти существующего врага сразу появлялся новый. В Unity это легко делается с помощью механизма шаблонов экземпляров (prefabs).

3.4.1. Что такое шаблон экземпляров?

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

влюбую сцену. Копирование может осуществляться вручную, чтобы гарантировать идентичность объектов-врагов (или других объектов) в каждой сцене. Но куда важнее то, что эти шаблоны могут порождаться кодом; поместить копии объектов

всцену можно не только вручную в визуальном редакторе, но и с помощью команд сценария.

ОПРЕДЕЛЕНИЕ  Ресурсом (asset) называется любой файл, отображаемый на вкладке Project; это могут быть двухмерные изображения, трехмерные модели, файлы с кодом, сцены и т. п. Термин «ресурс» вскользь упоминался в главе 1, но до текущего момента мы не акцентировали на нем внимания.

Термин экземпляр (instance) относится также к создаваемым на основе класса объектам кода. Попытайтесь не запутаться в терминологии; словосочетание «шаблон экземпляра» относится к игровому объекту, существующему вне сцены, в то время как экземпляром называется помещенная в сцену копия объекта.

3.4. Увеличение количества врагов      79

3.4.2. Создание шаблона врага

Конструирование шаблона начинается с создания объекта. Так как мы планируем получить шаблон из объекта-врага, этот шаг уже сделан. Теперь нужно перетащить строку с названием этого объекта с вкладки Hierarchy на вкладку Project, как показано на рис. 3.7. Объект автоматически сохранится в качестве шаблона. На панели Hierarchy его имя будет выделено синим цветом, означающим, что теперь он связан с шаблоном экземпляров. Редактирование этого шаблона (например, добавление компонентов) осуществляется посредством редактирования объекта сцены, после чего в меню GameObject выбирается команда Apply Changes To Prefab. Но сейчас данный объект в сцене уже не нужен (мы собираемся порождать новые шаблоны, а не пользоваться уже имеющимся экземпляром), поэтому его следует удалить.

Д а а а а

а Hierarchy а а Project

Рис. 3.7. Процесс получения шаблона экземпляров

ВНИМАНИЕ  Интерфейс для работы с шаблонами экземпляров не слишком удобен, а соотношения между шаблонами и их экземплярами в сценах порой достаточно нестабильны. К примеру, зачастую требуется перетащить шаблон в сцену для последующего редактирования, а после завершения этого процесса удалить объект. В первой главе я упоминал об этом как о недостатке Unity и надеюсь, что в следующих версиях последовательность действий будет усовершенствована.

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

3.4.3. Экземпляры невидимого компонента SceneController

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

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

80      Глава 3. Добавляем в игру врагов и снаряды

Выберите в меню GameObject команду Create Empty и присвойте новому объекту имя Controller. Убедитесь, что он находится в точке с координатами 0, 0, 0 (с технической точки зрения местоположение этого объекта не имеет значения, так как его все равно не видно, но, поместив его в начало координат, вы облегчите себе жизнь, если в будущем решите использовать его в цепочке наследования). Создайте сценарий SceneController, показанный в следующем листинге.

Листинг 3.10. Сценарий SceneController, порождающий экземпляры врагов

using

UnityEngine;

 

using

System.Collections;

 

public class SceneController : MonoBehaviour {

Сериализованная переменная для связи

[SerializeField] private GameObject enemyPrefab; ¬ с объектом-шаблоном. private GameObject _enemy; ¬ Закрытая переменная для слежения за экземпляром врага в сцене.

void Update() { ¬ Порождаем нового врага, только если враги в сцене отсутствуют. if (_enemy == null) {

_enemy = Instantiate(enemyPrefab) as GameObject; ¬ Метод, копирующий объект-шаблон. _enemy.transform.position = new Vector3(0, 1, 0);

float angle = Random.Range(0, 360); _enemy.transform.Rotate(0, angle, 0);

}

}

}

Присоедините этот сценарий к объекту-контроллеру, и на панели Inspector появится поле для шаблона врага с именем Enemy Prefab. Оно работает аналогично общедоступным переменным, но есть и важное отличие.

ВНИМАНИЕ  Для ссылки на объекты в редакторе Unity я рекомендую закрытые переменные с атрибутом SerializeField, так как нам нужно отобразить поле новой переменной на панели Inspector, но при этом не хотелось бы, чтобы ее значение могли менять другие сценарии. Как объяснялось в главе 2, открытые переменные отображаются на панели Inspector по умолчанию (другими словами, их сериализацией занимается Unity), поэтому в большинстве руководств и примеров для всех сериализованных значений фигурируют общедоступные переменные. Но эти переменные могут быть модифицированы другими сценариями (ведь они являются общедоступными); в большинстве же случаев редактирование значений должно разрешаться только через панель Inspector.

Перетащите шаблон врага с вкладки Project на пустое поле переменной; при наведении на него указателя мыши вы должны увидеть, как поле подсвечивается, демонстрируя допустимость присоединения объекта (рис. 3.8). После присоединения к шаблону врага сценария SceneController воспроизведите сцену, чтобы посмотреть, как работает код. Враг, как и раньше, будет возникать в центре комнаты, но если вы его застрелите, на его месте появится новый враг. Это намного лучше, чем единственный враг, который умирает навсегда!

СОВЕТ  Перетаскивание объектов на поля переменных панели Inspector — весьма удобный прием, используемый в самых разных сценариях. В данном случае мы связали шаблон со сценарием, но можно связывать между собой объекты сцены и даже отдельные компоненты (так как код в этом конкретном компоненте должен вызывать открытые методы). В следующих главах мы еще не раз воспользуемся этим приемом.

3.4. Увеличение количества врагов      81

П а а а Project а

а Inspector

Рис. 3.8. Процедура соединения шаблона со сценарием

Центральной частью сценария является метод Instantiate(), поэтому обратите внимание на содержащую его строку. Созданные экземпляры шаблона появляются в сцене. По умолчанию метод Instantiate() возвращает новый объект обобщенного типа Object, который напрямую практически бесполезен, поэтому его следует обрабатывать как объект GameObject. В языке C# приведение типов осуществляется при помощи ключевого слова as (указывается исходный объект, ключевое слово as и желаемый новый тип).

Полученный экземпляр сохраняется в закрытой переменной _enemy типа GameObject (снова напоминаю о разнице между шаблоном экземпляра и самим экземпляром; переменная enemyPrefab хранит в себе шаблон, в то время как переменная _enemy — экземпляр этого шаблона). Инструкция if, проверяющая сохраненный объект, гарантирует, что метод Instantiate() будет вызван только при пустой переменной _enemy (или на языке кода — при равенстве этой переменной значению null). Изначально эта переменная пуста, соответственно, код создания экземпляра запускается в самом начале сеанса. Возвращенный методом Instantiate() объект затем сохраняется в переменной _enemy, блокируя повторный запуск кода создания экземпляров.

После попадания объект-враг разрушается, переменная _enemy становится пустой, что приводит к вызову метода Instantiate(). Благодаря этому враг всегда присутствует в сцене.

РАЗРУШЕНИЕ ИГРОВЫХ ОБЪЕКТОВ И УПРАВЛЕНИЕ ПАМЯТЬЮ

Тот факт, что существующие ссылки при разрушении объекта начинают указывать на значение null, является до некоторой степени неожиданным. В языках программирования с автоматическим управлением памятью, к которым относится C#, мы, как правило, не можем напрямую удалять объекты; можно обнулить все ведущие на них ссылки, после чего удаление объекта произойдет автоматически. Это верно и в Unity, но фоновый способ обработки объектов GameObject выглядит в Unity так, как если бы удаление совершалось напрямую.

Для отображения объектов в Unity ссылки на все эти объекты должны присутствовать в графе сцены. Соответственно, даже после удаления всех ссылок на конкретный игровой объект вкоде на него все равно будет ссылаться граф сцены, что сделает автоматическое удаление невозможным.