
- •Предисловие
- •Введение
- •Благодарности
- •О книге
- •Перспективы
- •Условные обозначения, требования и доступные для скачивания данные
- •Автор в Интернете
- •Об авторе
- •Глава 1. Знакомство с Unity
- •1.1. Достоинства Unity
- •1.1.1. Сильные стороны и преимущества Unity
- •1.1.2. Недостатки, о которых нужно знать
- •1.1.3. Примеры игр на основе Unity
- •1.2. Как работать с Unity
- •1.2.1. Вкладка Scene, вкладка Game и панель инструментов
- •1.2.2. Работа с мышью и клавиатурой
- •1.2.3. Вкладка Hierarchy и панель Inspector
- •1.2.4. Вкладки Project и Console
- •1.3. Готовимся программировать в Unity
- •1.3.1. Запуск кода в Unity: компоненты сценария
- •1.3.2. Программа MonoDevelop — межплатформенная среда разработки
- •1.4. Заключение
- •Глава 2. Создание 3D-ролика
- •2.1. Подготовка…
- •2.1.1. Планирование проекта
- •2.1.2. Трехмерное координатное пространство
- •2.2. Начало проекта: размещение объектов
- •2.2.1. Декорации: пол, внешние и внутренние стены
- •2.2.2. Источники света и камеры
- •2.2.3. Коллайдер и точка наблюдения игрока
- •2.3. Двигаем объекты: сценарий, активирующий преобразования
- •2.3.1. Схема программирования движения
- •2.3.2. Написание кода
- •2.3.3. Локальные и глобальные координаты
- •2.4. Компонент сценария для осмотра сцены: MouseLook
- •2.4.1. Горизонтальное вращение, следящее за указателем мыши
- •2.4.2. Поворот по вертикали с ограничениями
- •2.4.3. Одновременные горизонтальное и вертикальное вращения
- •2.5. Компонент для клавиатурного ввода
- •2.5.1. Реакция на нажатие клавиш
- •2.5.2. Независимая от скорости работы компьютера скорость перемещений
- •2.5.4. Ходить, а не летать
- •2.6. Заключение
- •3.1. Стрельба путем бросания лучей
- •3.1.1. Что такое бросание лучей?
- •3.1.2. Имитация стрельбы командой ScreenPointToRay
- •3.1.3. Добавление визуальных индикаторов для прицеливания и попаданий
- •3.2. Создаем активные цели
- •3.2.1. Определяем точку попадания
- •3.2.2. Уведомляем цель о попадании
- •3.3. Базовый искусственный интеллект для перемещения по сцене
- •3.3.1. Диаграмма работы базового искусственного интеллекта
- •3.3.2. «Поиск» препятствий методом бросания лучей
- •3.3.3. Слежение за состоянием персонажа
- •3.4.1. Что такое шаблон экземпляров?
- •3.4.2. Создание шаблона врага
- •3.4.3. Экземпляры невидимого компонента SceneController
- •3.5. Стрельба путем создания экземпляров
- •3.5.1. Шаблон снаряда
- •3.5.2. Стрельба и столкновение с целью
- •3.5.3. Повреждение игрока
- •3.6. Заключение
- •Глава 4. Работа с графикой
- •4.1. Основные сведения о графических ресурсах
- •4.2. Создание геометрической модели сцены
- •4.2.1. Назначение геометрической модели
- •4.2.2. Рисуем план уровня
- •4.2.3. Расставляем примитивы в соответствии с планом
- •4.3. Наложение текстур
- •4.3.1. Выбор формата файла
- •4.3.2. Импорт файла изображения
- •4.3.3. Назначение текстуры
- •4.4. Создание неба с помощью текстур
- •4.4.1. Что такое скайбокс?
- •4.4.2. Создание нового материала для скайбокса
- •4.5. Собственные трехмерные модели
- •4.5.1. Выбор формата файла
- •4.5.2. Экспорт и импорт модели
- •4.6. Системы частиц
- •4.6.1. Редактирование параметров эффекта
- •4.6.2. Новая текстура для пламени
- •4.6.3. Присоединение эффектов частиц к трехмерным объектам
- •4.7. Заключение
- •5.1. Подготовка к работе с двухмерной графикой
- •5.1.1. Подготовка проекта
- •5.1.2. Отображение двухмерных изображений (спрайтов)
- •5.1.3. Переключение камеры в режим 2D
- •5.2. Создание карт и превращение их в интерактивные объекты
- •5.2.1. Создание объекта из спрайтов
- •5.2.2. Код ввода с помощью мыши
- •5.2.3. Открытие карты по щелчку
- •5.3. Отображение различных карт
- •5.3.1. Программная загрузка изображений
- •5.3.3. Создание экземпляров карт
- •5.3.4. Тасуем карты
- •5.4. Совпадения и подсчет очков
- •5.4.1. Сохранение и сравнение открытых карт
- •5.4.2. Скрытие несовпадающих карт
- •5.4.3. Текстовое отображение счета
- •5.5. Кнопка Restart
- •5.5.1. Добавление к компоненту UIButton метода SendMessage
- •5.5.2. Вызов метода LoadLevel в сценарии SceneController
- •5.6. Заключение
- •Глава 6. Двухмерный GUI для трехмерной игры
- •6.1. Перед тем как писать код…
- •6.1.1. IMGUI или усовершенствованный 2D-интерфейс?
- •6.1.2. Выбор компоновки
- •6.1.3. Импорт изображений UI
- •6.2. Настройка GUI
- •6.2.1. Холст для интерфейса
- •6.2.2. Кнопки, изображения и текстовые подписи
- •6.2.3. Управление положением элементов UI
- •6.3. Программирование интерактивного UI
- •6.3.1. Программирование невидимого объекта UIController
- •6.3.2. Создание всплывающего окна
- •6.3.3. Задание значений с помощью ползунка и поля ввода
- •6.4. Обновление игры в ответ на события
- •6.4.1. Интегрирование системы сообщений
- •6.4.2. Рассылка и слушание сообщений сцены
- •6.4.3. Рассылка и слушание сообщений проекционного дисплея
- •6.5. Заключение
- •7.1. Корректировка положения камеры
- •7.1.1. Импорт персонажа
- •7.1.2. Добавление в сцену теней
- •7.1.3. Облет камеры вокруг персонажа
- •7.2. Элементы управления движением, связанные с камерой
- •7.2.1. Поворот персонажа лицом в направлении движения
- •7.2.2. Движение вперед в выбранном направлении
- •7.3. Выполнение прыжков
- •7.3.1. Добавление вертикальной скорости и ускорения
- •7.3.2. Распознавание поверхности с учетом краев и склонов
- •7.4. Анимация персонажа
- •7.4.1. Создание анимационных клипов для импортированной модели
- •7.4.2. Создание контроллера для анимационных клипов
- •7.4.3. Код, управляющий контроллером-аниматором
- •7.5. Заключение
- •8.1. Создание дверей и других устройств
- •8.1.1. Открывание и закрывание дверей
- •8.1.2. Проверка расстояния и направления перед открытием двери
- •8.1.3. Управление меняющим цвет монитором
- •8.2. Взаимодействие с объектами путем столкновений
- •8.2.1. Столкновение с препятствиями, обладающими физическими свойствами
- •8.2.2. Управление дверью с помощью триггера
- •8.2.3. Сбор разбросанных по игровому уровню элементов
- •8.3. Управление инвентаризационными данными и состоянием игры
- •8.3.1. Настраиваем диспетчеры игрока и инвентаря
- •8.3.2. Программирование диспетчеров
- •8.3.3. Сохранение инвентаря в виде коллекции: списки и словари
- •8.4. Интерфейс для использования и подготовки элементов
- •8.4.1. Отображение элементов инвентаря в UI
- •8.4.2. Подготовка ключа для открытия двери
- •8.4.3. Восстановление здоровья персонажа
- •8.5. Заключение
- •9.1. Создание натурной сцены
- •9.1.1. Генерирование неба с помощью скайбокса
- •9.1.2. Настройка управляемой кодом атмосферы
- •9.2. Скачивание сводки погоды из Интернета
- •9.2.1. Запрос веб-данных через сопрограмму
- •9.2.2. Парсинг текста в формате XML
- •9.2.3. Парсинг текста в формате JSON
- •9.2.4. Изменение вида сцены на базе данных о погоде
- •9.3. Добавление рекламного щита
- •9.3.1. Загрузка изображений из Интернета
- •9.3.2. Вывод изображения на щите
- •9.3.3. Кэширование скачанного изображения
- •9.4. Отправка данных на веб-сервер
- •9.4.1. Слежение за погодой: отправка запросов POST
- •9.4.2. Серверный код в PHP-сценарии
- •9.5. Заключение
- •Глава 10. Звуковые эффекты и музыка
- •10.1. Импорт звуковых эффектов
- •10.1.1. Поддерживаемые форматы файлов
- •10.1.2. Импорт аудиофайлов
- •10.2. Воспроизведение звуковых эффектов
- •10.2.1. Система воспроизведения: клипы, источник, подписчик
- •10.2.2. Присваивание зацикленного звука
- •10.2.3. Активация звуковых эффектов из кода
- •10.3. Интерфейс управления звуком
- •10.3.1. Настройка центрального диспетчера управления звуком
- •10.3.2. UI для управления громкостью
- •10.3.3. Воспроизведение звуков UI
- •10.4. Фоновая музыка
- •10.4.1. Воспроизведение музыкальных циклов
- •10.4.2. Отдельная регулировка громкости
- •10.4.3. Переход между песнями
- •10.5. Заключение
- •Глава 11. Объединение фрагментов в готовую игру
- •11.1. Построение ролевого боевика изменением назначения проектов
- •11.1.1. Сборка ресурсов и кода из разных проектов
- •11.1.2. Элементы наведения и щелчка
- •11.1.3. Замена старого GUI новым
- •11.2. Разработка общей игровой структуры
- •11.2.1. Управление ходом миссии и набором уровней
- •11.2.2. Завершение уровня
- •11.2.3. Проигрыш уровня
- •11.3. Обработка хода игры
- •11.3.1. Сохранение и загрузка достижений игрока
- •11.3.2. Победа в игре при прохождении всех уровней
- •11.4. Заключение
- •Глава 12. Развертывание игр на устройствах игроков
- •12.1. Создание приложений для настольных компьютеров: Windows, Mac и Linux
- •12.1.1. Построение приложения
- •12.1.2. Настройки проигрывателя: имя и значок приложения
- •12.1.3. Компиляция в зависимости от платформы
- •12.2. Создание игр для Интернета
- •12.2.1. Проигрыватель Unity и HTML5/WebGL
- •12.2.2. Создание файла Unity и тестовой веб-страницы
- •12.2.3. Обмен данными с JavaScript в браузере
- •12.3. Сборки для мобильных устройств: iOS и Android
- •12.3.1. Настройка инструментов сборки
- •12.3.2. Сжатие текстур
- •12.3.3. Разработка подключаемых модулей
- •12.4. Заключение
- •Приложение А. Перемещение по сцене и клавиатурные комбинации
- •А.1. Навигация с помощью мыши
- •А.2. Распространенные клавиатурные комбинации
- •Б.1. Инструменты программирования
- •Б.1.1. Visual Studio
- •Б.1.2. Xcode
- •Б.1.3. Android SDK
- •Б.1.4. SVN, Git или Mercurial
- •Б.2. Приложения для работы с трехмерной графикой
- •Б.2.1. Maya
- •Б.2.3. Blender
- •Б.3. Редакторы двухмерной графики
- •Б.3.1. Photoshop
- •Б.3.2. GIMP
- •Б.3.3. TexturePacker
- •Б.4. Звуковое программное обеспечение
- •Б.4.1. Pro Tools
- •Б.4.2. Audacity
- •Приложение В. Моделирование скамейки в программе Blender
- •В.1. Создание сеточной геометрии
- •В.2. Назначение материала

208 Глава 8. Добавление в игру интерактивных устройств и элементов
много кода. Если бы это была единственная стоявшая перед нами цель, решение действительно можно было бы назвать излишне сложным. Но смысл всей этой тщательно разрабатываемой архитектуры кода в том, чтобы сохранять все данные в отдельных гибких модулях. Полезность данного паттерна становится очевидной по мере усложнения игры. Сейчас мы напишем пользовательский интерфейс, и управлять отдельными частями кода станет намного проще.
8.4. Интерфейс для использования и подготовки элементов
В игре коллекцией собранных элементов можно пользоваться по-разному, но в любом случае вам потребуется какой-то пользовательский интерфейс, чтобы игрок мог видеть имеющийся у него инвентарь. После этого мы добавим к этому интерфейсу интерактивность, позволив игрокам применять элементы. Мы снова рассмотрим пару конкретных примеров (подготовку ключа и применение пакетов здоровья), после чего вы самостоятельно сможете адаптировать этот код для элементов других типов.
ПРИМЕЧАНИЕ Как упоминалось в главе 6, в Unity есть как более старый GUI непосредственного режима, так и более новая система UI на основе спрайтов. В этой главе мы задействуем GUI непосредственного режима, поскольку этот интерфейс быстрее в реализации и требует меньшего количества настроек, что хорошо подходит для упражнений. При этом UI на основе спрайтов имеет более завершенный вид, поэтому этот интерфейс выбирают для итогового варианта игры.
8.4.1. Отображение элементов инвентаря в UI
Для отображения информации об элементах инвентаря в UI нужно добавить пару методов в сценарий InventoryManager. В настоящее время список элементов является закрытым и доступен только в соответствующем диспетчере; чтобы сделать его видимым, потребуются открытые методы доступа к данным. Добавьте их из следующего листинга.
Листинг 8.17. Добавление в сценарий InventoryManager методов доступа к данным
...
public List<string> GetItemList() {
List<string> list = new List<string>(_items.Keys); ¬ Возвращаем список всех ключей словаря. return list;
}
public int GetItemCount(string name) { ¬ Возвращаем количество указанных элементов в инвентаре. if (_items.ContainsKey(name)) {
return _items[name];
}
return 0;
}
...
Метод GetItemList() возвращает список элементов инвентаря. У вас может появиться вопрос, зачем мы ранее тратили столько сил на преобразование списка элементов инвентаря в другой формат. Дело в том, что теперь элемент каждого типа появляется

8.4. Интерфейс для использования и подготовки элементов 209
в списке всего один раз. Например, при наличии в инвентаре двух пакетов здоровья, имя «health» в списке будет всего одно. Ведь этот список был создан из ключей элементов словаря, а не из отдельных элементов.
Метод GetItemCount() возвращает количество элементов указанного типа в инвентаре. Например, вызов GetItemCount("health") представляет собой вопрос «Сколько пакетов здоровья содержит инвентарь?». Это даст возможность отображать в UI не только все элементы, но и их количество.
Благодаря наличию этих методов в сценарии InventoryManager, мы можем создать пользовательский интерфейс. Давайте отобразим все его элементы в виде горизонтальной панели в верхней части экрана. Каждому элементу будет соответствовать свой значок, поэтому нужно импортировать в проект соответствующие изображения. Ресурсы, находящиеся в папке Resources, Unity обрабатывает особым способом.
СОВЕТ Ресурсы из папки Resources доступны для загрузки методом Resources.Load(). В остальных случаях для вставки их в сцену пользуйтесь редактором Unity.
Рисунок 8.8 демонстрирует четыре значка вместе с адресом папки, в которой они хранятся. Создайте папку Resources, а затем внутри нее — папку Icons.
Рис. 8.8. Графические ресурсы для значков оборудования в папке Resources
После этого создайте пустой объект GameObject с именем Controller и свяжите с ним новый сценарий BasicUI (его код представлен в следующем листинге).
Листинг 8.18. Сценарий BasicUI, отображающий инвентарь
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class BasicUI : MonoBehaviour { void OnGUI() {
int posX = 10; int posY = 10; int width = 100; int height = 30; int buffer = 10;
List<string> itemList = Managers.Inventory.GetItemList();
if (itemList.Count == 0) { ¬ Отображает сообщение, информирующее об отсутствии инвентаря.
GUI.Box(new Rect(posX, posY, width, height), "No Items");
}
foreach (string item in itemList) {
int count = Managers.Inventory.GetItemCount(item);

210 Глава 8. Добавление в игру интерактивных устройств и элементов
Texture2D image = Resources.Load<Texture2D>("Icons/"+item); ¬ Метод, загружающий ресурсы
GUI.Box(new Rect(posX, posY, |
width, height), |
из папки Resources. |
new GUIContent("(" + count + |
")", image)); |
|
posX += width+buffer; ¬ При каждом прохождении цикла сдвигаемся в сторону. |
|
}
}
}
Этот листинг отображает собранные элементы в виде горизонтальной полосы, показанной на рис. 8.9, показывая заодно их количество. Как упоминалось в главе 3, все представители класса MonoBehaviour автоматически отвечают на метод OnGUI(), после визуализации сцены запускаемый в каждом кадре.
Рис. 8.9. Отображение инвентаря средствами UI
Внутри метода OnGUI() первым делом определяется набор значений для размещения UI-элементов. Эти значения увеличиваются в процессе циклического просмотра всех элементов, что позволяет расположить их в ряд. Рисуемые UI-элементы — это в данном случае GUI.Box; они представляют собой неинтерактивные поля, в которых отображаются текст и значки.
Метод Resources.Load() загружает ресурсы из папки Resources. Это удобный способ загрузки ресурсов по именам; обратите внимание, что имя каждого элемента передается как параметр. Нужно указать тип загружаемых ресурсов, в противном случае метод вернет обобщенные объекты.
Пользовательский интерфейс показывает нам собранный инвентарь. Теперь мы можем им воспользоваться.
8.4.2. Подготовка ключа для открытия двери
Рассмотрим пару примеров применения инвентаря, чтобы вы смогли потом по аналогии работать с элементами любых типов. Начнем мы с подготовки ключа, который потребуется нам для открытия двери.
В настоящее время сценарий DeviceTrigger нашего инвентаря не видит (ведь он был написан до появления связанного с инвентарем кода). Коррективы, которые следует внести в сценарий, показаны в следующем листинге.

8.4. Интерфейс для использования и подготовки элементов 211
Листинг 8.19. Запрос ключа в сценарии DeviceTrigger
...
public bool requireKey;
void OnTriggerEnter(Collider other) {
if (requireKey && Managers.Inventory.equippedItem != "key") { return;
}
...
Как видите, вам потребовались всего лишь новая открытая переменная и условная инструкция для поиска подготовленного к использованию ключа. Логическая переменная requireKey появляется в виде флажка на панели Inspector, что позволяет требовать ключ только от определенных триггеров. Условие в начале метода OnTriggerEnter() проверяет наличие подготовленного ключа в диспетчере InventoryManager; соответственно, нужно добавить в сценарий InventoryManager код следующего листинга.
Листинг 8.20. Код подготовки инвентаря для сценария InventoryManager
...
public string equippedItem {get; private set;}
...
public bool EquipItem(string name) {
if (_items.ContainsKey(name) && equippedItem != name) { ¬ equippedItem = name;
Debug.Log("Equipped " + name); return true;
}
equippedItem = null; Debug.Log("Unequipped"); return false;
}
...
Проверяем наличие в инвентаре указанного элемента и тот факт, что он еще не подготовлен к использованию.
В верхнюю часть сценария добавлено свойство equippedItem, которое проверяется другим фрагментом кода. Затем мы добавили открытый метод EquipItem(), позволяющий другому коду менять подготовленные элементы. Этот код готовит элемент, если он еще не готов к применению, или кладет его обратно в инвентарь, если пользоваться им уже можно.
Напоследок, чтобы у игрока появилась возможность выполнять подготовку элементов, указанная функциональность добавляется в UI. Это делается в следующем фрагменте кода.
Листинг 8.21. Добавление к сценарию BasicUI функции подготовки элементов
... |
Написанный курсивом код в сценарии уже присутствовал, |
foreach (string item in itemList) { |
¬ тут он фигурирует в качестве точки отсчета. |
int count = Managers.Inventory.GetItemCount(item); GUI.Box(new Rect(posX, posY, width, height), item +
"(" + count + ")"); posX += width+buffer;
}