- •Предисловие
- •Введение
- •Благодарности
- •О книге
- •Перспективы
- •Условные обозначения, требования и доступные для скачивания данные
- •Автор в Интернете
- •Об авторе
- •Глава 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. Назначение материала
194 Глава 8. Добавление в игру интерактивных устройств и элементов
8.2. Взаимодействие с объектами путем столкновений
В предыдущем разделе управление устройствами осуществлялось путем клавиатурного ввода, но это не единственный способ взаимодействия персонажа с элементами уровня. Другим крайне эффективным подходом является реакция на столкновение с персонажем. Большую часть операций при этом выполняет Unity благодаря встроенным в игровой движок механизмам распознавания столкновений и имитации физических явлений. При этом, хотя Unity поможет вам распознать столкновение, никто кроме вас не сможет запрограммировать реакцию объекта.
Мы рассмотрим три варианта реакции на столкновение, встречающиеся в играх:Толчок и падение.
Срабатывание устройства.
Исчезновение в момент контакта (при сборе элементов).
8.2.1. Столкновение с препятствиями, обладающими физическими свойствами
Первым делом мы создадим набор поставленных друг на друга коробок, которые будут разлетаться по сцене после того, как в них врежется персонаж. Моделирование подобной сцены требует сложных физических расчетов, но все они встроены в Unity, обеспечивая реалистичный вид разлетающихся коробок.
По умолчанию объекты в Unity не имеют физических свойств реальных объектов. Эта функция включается добавлением компонента Rigidbody. Собственно, именно с этого мы начали обсуждение проекта в главе 3, ведь бросаемым врагами огненным шарам был нужен этот компонент. Как я тогда объяснял, в Unity система имитации физических явлений работает только с объектами, оснащенными компонентом Rigidbody. Щелкните на кнопке Add Component и найдите вариант Rigidbody в разделе Physics.
Создайте новый куб и добавьте к нему компонент Rigidbody. Вам потребуется несколько таких кубов, положенных друг на друга. Например, я создал пять кубов и расположил их в виде двух рядов, как показано на рис. 8.3.
У а а RigidBody. В а :
–4,2 ,5 –2,3 –4,2 ,5 –1,2 –4,2 ,5 –,1 –4,2 1,5 –1,9 –4,2 1,5 –,7
Рис. 8.3. Штабель из пяти коробок, в который мы будем врезаться
8.2. Взаимодействие с объектами путем столкновений 195
Кубы готовы реагировать на приложенную к ним силу. Чтобы источником этой силы стал персонаж, внесите дополнение из следующего листинга в присоединенный к персонажу сценарий RelativeMovement (это один из сценариев, написанных в предыдущей главе).
Листинг 8.5. Добавление физической силы в сценарий RelativeMovement
...
public float pushForce = 3.0f; ¬ Величина прилагаемой силы.
...
void OnControllerColliderHit(ControllerColliderHit hit) { _contact = hit;
|
|
Проверка, есть ли у участвующего в столкновении |
Rigidbody body = |
hit.collider.attachedRigidbody; ¬ объекта компонент Rigidbody, обеспечивающий |
|
if (body != null |
&& !body.isKinematic) { |
реакцию на приложенную силу. |
body.velocity = hit.moveDirection * pushForce; ¬ Назначение физическому телу скорости.
}
}
...
Особых объяснений этот код не требует: при любом столкновении персонажа с объектом проверяется наличие у этого объекта компонента Rigidbody. В случае положительного результата проверки этому компоненту присваивается указанная скорость.
Запустите игру и заставьте персонажа бежать прямо на штабель — штабель вполне реалистично рассыплется. Как видите, имитация физического явления не потребовала от вас особых усилий! Благодаря встроенному в Unity механизму имитации физики, нам не приходится писать объемный код. Мы легко заставили объекты двигаться в ответ на столкновение, но есть и другой вариант реакции — генерация события срабатывания. Воспользуемся таким событием для управления дверью.
8.2.2. Управление дверью с помощью триггера
Если раньше управление дверью осуществлялось посредством нажатия клавиши, теперь она будет открываться и закрываться в ответ на столкновение персонажа с другим объектом сцены. Создайте еще одну дверь и поместите ее в другой проем (я сделал копию уже существующей двери и поместил ее в точку с координатами –2.5, 1.5, –17). Теперь создайте еще один куб, который будет играть роль триггера, и установите в свитке Box Collider флажок Is Trigger (вы уже делали подобное, работая над огненными шарами в главе 3). Кроме того, выберите в меню Layer в верхнем правом углу панели Inspector команду Ignore Raycast. Напоследок следует отключить у этого объекта возможность формирования теней (напоминаю, что это делается в свитке Mesh Renderer).
ВНИМАНИЕ Об этих небольших операциях легко забыть, меж тем как они крайне важны: вы не сможете использовать объект в качестве триггера, если не установлен флажок Is Trigger. Кроме того, обязательно укажите для слоя настройку Ignore Raycast, чтобы наш триггер не учитывался в операциях, связанных с бросанием лучей.
ПРИМЕЧАНИЕ Когда в главе 3 вы в первый раз столкнулись с объектами-триггерами, к ним требовалось добавить компонент Rigidbody. Теперь же он не требуется, так как триггеру не нужно реагировать на действия игрока (раньше у нас была реакция на столкновения со стенами). Сейчас же
196 Глава 8. Добавление в игру интерактивных устройств и элементов
для работы триггера нужно, чтобы у него или у входящего с ним в контакт объекта была включена система имитации физических явлений; под это требование подпадает не только компонент Rigidbody, но и назначенный персонажу компонент CharacterController.
Расположите объект так и придайте ему такой размер, чтобы он включал в себя дверь
инекоторую область вокруг двери; я ввел в поля Position значения –2.5, 1.5, –17 (как у самой двери), а в поля Scale — значения 7.5, 3, 6. Кроме того, имеет смысл назначить этому объекту полупрозрачный материал, чтобы пространство триггера было легко отличить от остальных объектов. Создайте новый материал командой меню Assets
ивыделите его на вкладке Project. В верхней части панели Inspector находится раскрывающийся список Rendering Mode (там сейчас выбрано значение Opaque, предлагаемое по умолчанию), в котором нужно выбрать вариант Transparent.
Теперь щелкните на поле с каталогом цветов для вызова окна выбора цвета. В основной части окна выберите зеленый цвет и, используя нижний ползунок, уменьшите значение канала прозрачности. Перетащите материал со вкладки Project на объект. Рисунок 8.4 демонстрирует вид триггера после назначения материала.
О а а а ,
а а
Рис. 8.4. Зона триггера, окружающая дверь, которой он управляет
ОПРЕДЕЛЕНИЕ Триггеры часто называются зонами, а не объектами, чтобы провести грань между твердыми телами и объектами, через которые вы можете пройти.
Запустите игру и убедитесь, что вы без проблем проходите через зону триггера; столкновения с объектом при этом регистрируются, но они больше не влияют на движение персонажа. Реакция на такого рода столкновения задается программно. В частности, нам нужно, чтобы триггер управлял дверью. Поэтому создайте новый сценарий DeviceTrigger и скопируйте в него код из следующего листинга.
Листинг 8.6. Код для триггера, контролирующего устройство
using UnityEngine;
using System.Collections;
public class DeviceTrigger : MonoBehaviour {
[SerializeField] private GameObject[] targets; ¬ Список целевых объектов, которые будет активировать данный триггер.
8.2. Взаимодействие с объектами путем столкновений 197
void OnTriggerEnter(Collider |
other) { ¬ |
foreach (GameObject target |
in targets) { |
target.SendMessage("Activate");
}
}
Метод OnTriggerEnter() вызывается при попадании объекта в зону триггера…
в то время как метод OnTriggerExit() вызывается void OnTriggerExit(Collider other) { ¬ при выходе объекта из зоны триггера.
foreach (GameObject target in targets) { target.SendMessage("Deactivate");
}
}
}
В этом листинге определяется массив целевых объектов; в большинстве случаев он будет состоять из одного элемента, но потенциально один триггер может управлять целым набором устройств. Цикл применяется для рассылки сообщений всем целевым объектам, причем как внутри метода OnTriggerEnter(), так и в методе OnTriggerExit(). Эти методы однократно вызываются при входе другого объекта в зону триггера и выходе из нее (вместо того, чтобы снова и снова вызываться, пока объект находится в зоне триггера).
Обратите внимание, что теперь рассылаются другие сообщения; на этот раз нам нужно определить для двери функции Activate() и Deactivate(). Добавьте в сценарий, присоединенный к двери, код следующего листинга.
Листинг 8.7. Добавление активирующей и деактивирующей функций в сценарий DoorOpenDevice
...
public void Activate() {
if (!_open) { ¬ Открывает дверь, только если она пока не открыта.
Vector3 pos = transform.position + dPos; transform.position = pos;
_open = true;
}
}
public void Deactivate() {
if (_open) { ¬ Аналогично, закрывает дверь только при условии, что она уже не закрыта.
Vector3 pos = transform.position - dPos; transform.position = pos;
_open = false;
}
}
...
Код новых методов Activate() и Deactivate() практически совпадает с кодом уже знакомого вам метода Operate(), просто теперь мы разделили функции открытия и закрытия двери, в то время как раньше у нас была одна функция, обрабатывающая обе операции.
Итак, у нас есть весь необходимый код, и теперь зону триггера можно заставить управлять состоянием двери. Свяжите сценарий DeviceTrigger с зоной триггера и свяжите дверь со свойством Targets; на панели Inspector сначала укажите размер