- •Предисловие
- •Введение
- •Благодарности
- •О книге
- •Перспективы
- •Условные обозначения, требования и доступные для скачивания данные
- •Автор в Интернете
- •Об авторе
- •Глава 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. Назначение материала
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) {