142 64 68MB
Russian Pages 298 [299] Year 2023
Энтони Дэвис, Трэвис Батист, Рассел Крейг, Райан Станкел
Разработка 3D-игр в Unity
Unity 3D Game Development Designed for passionate game developers – Engineered to build professional games Anthony Davis Travis Baptiste Russell Craig Ryan Stunkel
BIRMINGHAM—MUMBAI
Разработка 3D-игр в Unity Создан для увлеченных разработчиков игр. Разработан для создания профессиональных игр Энтони Дэвис Трэвис Батист Рассел Крейг Райан Станкел
Москва, 2023
УДК 004.921 ББК 32.972 Д25
Энтони Дэвис, Трэвис Батист, Рассел Крейг, Райан Станкел Д25 Разработка 3D-игр в Unity / пер. с англ. П. М. Бомбаковой. – М.: ДМК Пресс, 2023. – 298 с.: ил. ISBN 978-5-93700-254-9 Эта книга ведет читателя от изучения основ проектирования 3D-игр и написания скриптов на C# к разработке собственной игры с яркими персонажами, объектами и фоном, эффектным освещением, анимацией и звуковым оформлением. Показано, как настроить взаимодействие пользователей с игровым интерфейсом, протестировать игру и подготовить ее для презентации в студиях. Издание адресовано тем, кто заинтересован в создании 3D-игр, уже имеет некоторые знания в этой области и хочет приобрести практический опыт разработки.
УДК 004.921 ББК 32.972
First published in the English language under the title ‘Unity 3D Game Development’ – (978-1-80107-614-2) Все права защищены. Любая часть этой книги не может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
ISBN (анг.) 978-1-80107-614-2 ISBN (рус.) 978-5-93700-254-9
Copyright ©Packt Publishing 2022 © Оформление, издание, перевод, ДМК Пресс, 2023
Оглавление Предисловие от издательства...............................................................................13 Об авторах..............................................................................................................14 О рецензентах........................................................................................................16 Введение.................................................................................................................17
1. Введение в трехмерное пространство....................................... 21 Цель книги.........................................................................................................22 Основы 3D..........................................................................................................23 Система координат........................................................................................23 Векторы..........................................................................................................25 Камеры...........................................................................................................25 Faces, edges, vertices, meshes.........................................................................26 Материалы, текстуры и шейдеры.................................................................26 Физика Rigidbody...........................................................................................28 Обнаружение столкновений.........................................................................29 Интерфейс Unity................................................................................................29 Окно сцены и иерархия................................................................................30 Inspector.........................................................................................................31 Project window................................................................................................32 Окно Game......................................................................................................33 Package Manager.............................................................................................34 Основные концепции Unity..............................................................................36 Ассеты.............................................................................................................36 Сцены.............................................................................................................36 Игровые объекты...........................................................................................36 Компоненты...................................................................................................37 Скрипты.........................................................................................................37 Префабы.........................................................................................................38 Пакеты............................................................................................................38 Заключение........................................................................................................38
2. Дизайн и прототип......................................................................... 39 Основы игрового дизайна.................................................................................39
6 Оглавление Проектная документация игры....................................................................39 Обдуманные решения...................................................................................41 Итеративное производство..........................................................................41 Концепция......................................................................................................43 Первый проект в Unity......................................................................................45 Unity Hub........................................................................................................45 Выбор версии.................................................................................................45 Выбор шаблона..............................................................................................46 Scriptable Rendering Pipeline.........................................................................46 Встроенный рендеринг.............................................................................46 Универсальный рендеринг.......................................................................47 Рендеринг высокой четкости....................................................................47 Прототипирование............................................................................................47 Цифровое или бумажное создание..............................................................48 Grayboxing......................................................................................................48 Proof of Concept (PoC)....................................................................................49 Минимально жизнеспособный продукт (MVP)...........................................50 Вертикальный срез........................................................................................50 Заключение........................................................................................................50
3. Программирование....................................................................... 52 Настройка среды................................................................................................52 Среда Unity.....................................................................................................52 Основы...............................................................................................................54 Переменные...................................................................................................56 Типы данных..................................................................................................56 Bool.............................................................................................................56 Int................................................................................................................56 Float............................................................................................................57 String...........................................................................................................58 GameObject.................................................................................................58 Логика программирования...........................................................................58 Операторы if..............................................................................................59 While...........................................................................................................60 For...............................................................................................................61 For или While..............................................................................................61 Методы...........................................................................................................62 Заключение........................................................................................................64
4. Персонажи...................................................................................... 65 Дизайн и концепт..............................................................................................65 Время концепции!.............................................................................................66 Риггинг...............................................................................................................69 Мышление под анимирование.....................................................................69 Деформация...................................................................................................70 Иерархия........................................................................................................70 Кости или суставы.........................................................................................71
Оглавление 7
Прямая кинематика / инверсная кинематика.............................................72 Ограничения..................................................................................................73 Деформеры....................................................................................................74 Controls...........................................................................................................74 Анимация на основе физики........................................................................74 Система инверсной кинематики человека (HIK)........................................74 Анимация.......................................................................................................75 Контроллеры персонажа...................................................................................76 Встроенный контроллер персонажа............................................................77 Контроллер персонажа Rigidbody.................................................................77 Сценарий движения вашего персонажа..........................................................77 Первоначальная настройка в Unity..............................................................78 Бездействие...................................................................................................83 Точка ввода кода............................................................................................85 RequireComponent.........................................................................................85 Обновление кода...........................................................................................86 Методы...........................................................................................................88 Заключение........................................................................................................89 Присоединяйтесь к Discord!..............................................................................90
5. Окружающая среда....................................................................... 91 Эскизирование...................................................................................................92 Мудборды...........................................................................................................93 Режиссура...........................................................................................................95 Блокирование....................................................................................................95 Unity Terrain...................................................................................................96 Создание ландшафта.................................................................................96 Настройки..................................................................................................97 Рисуем ландшафт......................................................................................98 Отрисовка деревьев.................................................................................105 Детализация.............................................................................................106 3D-геометрия...............................................................................................108 ProBuilder.................................................................................................108 Готовые базовые формы.........................................................................116 Итерирование..................................................................................................117 Заключение......................................................................................................118
6. Взаимодействия и механика.....................................................119 Игровые циклы................................................................................................119 Инструментарий механик...............................................................................121 Управление ресурсами................................................................................121 Риск vs вознаграждения..............................................................................122 Пространственное воображение................................................................122 Коллекция....................................................................................................122 Исследование...............................................................................................122 Ограничения................................................................................................123 Проектирование и реализация.......................................................................123
8 Оглавление Наш проект.......................................................................................................125 Лестницы......................................................................................................125 Проектирование......................................................................................125 Реализация...............................................................................................126 Блокатор лестницы......................................................................................129 Кольца...........................................................................................................130 Проектирование......................................................................................130 Реализация...............................................................................................131 Ограниченные пространства......................................................................137 Проектирование......................................................................................137 Реализация...............................................................................................138 Области взаимодействия............................................................................138 Проектирование......................................................................................138 Реализация...............................................................................................139 Заключение......................................................................................................140
7. Взаимодействие RigidBodies и физики....................................141 Компонент Rigidbody ......................................................................................141 Mass..............................................................................................................142 Drag...............................................................................................................142 Angular Drag.................................................................................................142 Логическое значение Use Gravity...............................................................142 Логическое значение Is Kinematic..............................................................143 Interpolate....................................................................................................143 Обнаружение столкновений.......................................................................144 Discrete.....................................................................................................145 Continuous................................................................................................145 Continuous Dynamic.................................................................................146 Continuous Speculative.............................................................................146 Ограничения................................................................................................147 Info................................................................................................................147 Вопросы проектирования и реализации.......................................................148 Взаимодействие телекинеза и физики..........................................................148 Падающие камни.........................................................................................148 Проектирование......................................................................................148 Реализация...............................................................................................148 Сломанный пьедестал.................................................................................149 Проектирование......................................................................................149 Реализация...............................................................................................149 Последняя головоломка..............................................................................150 Проектирование......................................................................................150 Реализация...............................................................................................150 Заключение......................................................................................................158
8. Пользовательский интерфейс и меню.....................................159 Пользовательский интерфейс........................................................................160 Диегетический – повествовательное «да», внутреннее «да»....................161
Оглавление 9
Недиегетический – повествовательное «нет», внутреннее «нет»............162 Пространственный – повествовательное «нет», внутреннее «да»...........163 Мета – повествовательное «да», внутреннее «нет»...................................164 Элементы UI.....................................................................................................165 Главное меню...............................................................................................165 Инвентари....................................................................................................165 Здоровье.......................................................................................................166 Система взаимодействия с предметами....................................................166 UI в нашем проекте.........................................................................................166 Главное меню...............................................................................................166 Меню выхода...............................................................................................167 Пространственная подсказка.....................................................................168 Unity UI.............................................................................................................169 Система Unity Canvas..................................................................................170 Преобразование Rect...............................................................................171 Компонент Canvas...................................................................................173 Canvas Scaler ...........................................................................................175 Компонент Graphic Raycaster..................................................................177 Объекты пользовательского интерфейса Unity.........................................178 Реализация...................................................................................................181 Реализация главного меню.....................................................................181 Реализация книги....................................................................................183 Реализация UI-взаимодействия.............................................................184 Заключение......................................................................................................186
9. Визуальные эффекты.................................................................. 187 Обзор визуальных эффектов..........................................................................187 Shader Graph.....................................................................................................188 Настройки....................................................................................................189 Создание шейдера.......................................................................................190 Lit Shader Graph.......................................................................................190 Sprite Lit Shader Graph.............................................................................190 Sprite Unlit Shader Graph.........................................................................191 Unlit Shader Graph....................................................................................191 Интерфейс Shader Graph.............................................................................191 Master Stack..............................................................................................191 Blackboard.................................................................................................198 Graph Inspector.........................................................................................199 Main Preview.............................................................................................199 Nodes........................................................................................................200 Часто используемые ноды..........................................................................201 Add............................................................................................................201 Color..........................................................................................................202 Lerp...........................................................................................................202 Multiply.....................................................................................................203 Sample Texture 2D....................................................................................204 Saturate.....................................................................................................204
10 Оглавление Split...........................................................................................................205 UV..............................................................................................................205 Векторы....................................................................................................206 Системы частиц...............................................................................................206 Shuriken........................................................................................................206 VFX Graph.....................................................................................................207 Nodes........................................................................................................211 Заключение......................................................................................................211
10. Звуковые эффекты....................................................................212 Звуковой... дизайн?.........................................................................................212 Пять элементов звукового дизайна................................................................213 Источник......................................................................................................213 Огибающие..................................................................................................214 Атака.........................................................................................................214 Затухание.................................................................................................215 Высота тона..................................................................................................216 Частота.........................................................................................................216 Наслоение....................................................................................................218 Проектирование в большом масштабе..........................................................218 С какой стороны подойти к созданию звуков для игры...........................219 Реализация звукового дизайна нашего проекта...........................................219 Получение нашего первого звука для воспроизведения..........................219 Организация проекта..............................................................................220 Музыка.....................................................................................................220 2D-звуки.......................................................................................................221 3D-звуки.......................................................................................................222 Использование 3D-звуков.......................................................................222 Аудиослушатель, часть I..........................................................................222 Настройки 3D-звука....................................................................................223 Аудиослушатель, часть II.........................................................................224 3D-звуки окружающего мира в игре......................................................227 Заполнение окружающими звуками......................................................229 2D-атмосфера..........................................................................................229 Запуск звука через взаимодействие с персонажем.......................................229 Запуск звука через события Unity..........................................................230 Звуки вращения деталей головоломки..................................................231 Головоломка с деревом...........................................................................233 Заключение......................................................................................................233
11. Сборка и тестирование.............................................................234 Сборка из Unity................................................................................................234 Target platform.............................................................................................235 Architecture..................................................................................................236 Сервер...........................................................................................................236 Copy PDB files...............................................................................................236 Create Visual Studio Solution.......................................................................236
Оглавление 11
Development Build........................................................................................236 Autoconnect Profiler.....................................................................................236 Deep Profiling Support..................................................................................237 Script Debugging...........................................................................................237 Scripts Only Build..........................................................................................237 Метод сжатия...............................................................................................237 Тестирование...................................................................................................238 Тестирование функциональности..............................................................238 Тестирование производительности...........................................................239 Unity Profiler.............................................................................................239 Memory Profiler........................................................................................241 Frame debugger.........................................................................................242 Physics debugger и модуль Profiler .........................................................243 Плейтестинг.................................................................................................244 Продолжительное тестирование ...............................................................245 Тестирование локализации........................................................................246 Пользовательский опыт, или UX.....................................................................246 Брендинг......................................................................................................246 Дизайн..........................................................................................................246 Удобство использования.............................................................................247 Исходная проблема.....................................................................................247 Первая головоломка....................................................................................247 Введение во вторичную механику.............................................................248 Финальная головоломка.............................................................................249 Заключение......................................................................................................250
12. Последние штрихи....................................................................251 Обзор................................................................................................................251 Доработка ассетов...........................................................................................252 Стилизация ассетов.....................................................................................252 Детализация нормалей...............................................................................253 Чистка архитектуры....................................................................................256 Блендинг текстур.........................................................................................257 Беспорядок в окружающей среде...............................................................259 Детализация меша.......................................................................................259 Эффекты.......................................................................................................259 Блокировщик лестницы..........................................................................259 Система Shuriken – блокирующий слой частиц на лестнице...............263 VFX Graph – телекинез Мивари..............................................................267 Синематики.................................................................................................274 Вторичная анимация..................................................................................274 Освещение.......................................................................................................274 3D-форма.....................................................................................................274 Обеспечение настроения............................................................................275 Дизайн гейм-плея.......................................................................................275 Освещение Unity..........................................................................................275 Смешанное освещение...........................................................................276
12 Оглавление Световые зонды.......................................................................................280 Зонд отражения.......................................................................................281 Доработка звука...............................................................................................282 Триггер звука через события анимации................................................283 Маркировка анимации событиями для звука...........................................285 Рандомизированные звуки....................................................................288 Рандомизированная тональность..........................................................289 Заключение......................................................................................................290
13. Бонус: другие инструменты Unity!..........................................291 Игровые сервисы Unity...................................................................................291 Инструменты для мультиплеера................................................................291 Создание..................................................................................................291 Соединение..............................................................................................292 Взаимодействие.......................................................................................293 Плагин XR.....................................................................................................293 Агент со средствами машинного обучения...............................................294 Визуальный скриптинг Bolt........................................................................294 Flow Graphs...............................................................................................294 State Graphs..............................................................................................295 Live Editing...............................................................................................295 Debugging and Analysis............................................................................295 Codebase Compatibility............................................................................295 Ease of Use................................................................................................295 Заключение..........................................................................................................295 Предметный указатель........................................................................................296
Предисловие от издательства Отзывы и пожелания Мы всегда рады отзывам наших читателей. Расскажите нам, что вы думаете об этой книге – что понравилось или, может быть, не понравилось. Отзывы важны для нас, чтобы выпускать книги, которые будут для вас максимально полезны. Вы можете написать отзыв на нашем сайте www.dmkpress.com, зайдя на страницу книги и оставив комментарий в разделе «Отзывы и рецензии». Также можно послать письмо главному редактору по адресу [email protected]; при этом укажите название книги в теме письма. Если вы являетесь экспертом в какой-либо области и заинтересованы в издании новой книги, заполните форму на нашем сайте по адресу http://dmkpress.com/ authors/publish_book/ или напишите в издательство по адресу [email protected].
Список опечаток Хотя мы приняли все возможные меры для того, чтобы обеспечить высокое качество наших текстов, ошибки все равно случаются. Если вы найдете ошибку в одной из наших книг – возможно, ошибку в основном тексте или программном коде, – мы будем очень благодарны, если вы сообщите нам о ней. Сделав это, вы избавите других читателей от непонимания текста и поможете нам улучшить последующие издания этой книги. Если вы найдете какие-либо ошибки в коде, пожалуйста, сообщите о них главному редактору по адресу [email protected], и мы исправим их в следующих тиражах.
Нарушение авторских прав Пиратство в сети Интернет по-прежнему является насущной проблемой. Издательство «ДМК Пресс» очень серьезно относится к вопросам защиты авторских прав и лицензирования. Если вы знаете о незаконной публикации какой-либо из наших книг в сети Интернет, пожалуйста, пришлите нам ссылку на интернет-ресурс, чтобы мы могли применить санкции. Ссылку на подозрительные материалы можно прислать по адресу [email protected]. Мы высоко ценим любую помощь по защите наших авторов, благодаря которой мы можем предоставлять вам качественные материалы.
Об авторах Энтони Дэвис (Anthony Davis) – старший технический художник компании Accelerate Solutions из Орландо, штат Флорида. До Unity он работал в нескольких сферах: от военного ветерана и тренера по гимнастике до открытия студии разработки инди-игр и других сфер деятельности. Его работа в независимой и внештатной работе позволила многим изучить все аспекты разработки игр. В свободное время предпочитает играть в Dungeons and Dragons, заниматься искусством и планировать свой следующий проект. Хочу поблагодарить свою семью за предоставленное пространство для работы над этим проектом. Тика (Tica), спасибо, что заставляешь все двигаться. Джерин (Jehryn) и Кембер (Kember), спасибо за понимание, когда я работал над книгой вместо того, чтобы поиграть с вами в игры. Мосс (Mohss), работая над книгой, я думал о тебе без остановки. Очень надеюсь, что книга поможет вам при создании собственных игр. Трэвис Батист (Travis Baptiste): художник, ученый на протяжении всей жизни и геймер-любитель — вот лишь некоторые из терминов, которые можно использовать для описания Трэвиса. После службы в армии Трэвис поступил в Full Sail University, где изучал игровое искусство. После выпуска в 2015 году он работал внештатным 3D-моделлером, одновременно обучая своих двоих детей на дому. Между обслуживанием клиентов и личными проектами Трэвис поддерживал свои 3D-таланты в нескольких программах. Моя семья, спасибо вам за понимание, когда мне приходится работать. Моим сыновьям: я надеюсь, что этот опыт еще больше вдохновит вас обоих на достижение собственных целей, несмотря на трудности. Моей жене Альмире (Almira) спасибо за поддержку. Работать по ночам стало намного легче с помощью, которую ты оказала с детьми. Спасибо, Дэвид Нгуен (David Nguyen), за жизненные ориентиры и за то, что ты рядом. Спасибо, Энтони (Anthony), за предоставленную возможность. Я благодарен, что у меня была возможность работать с вами над проектом.
Об авторах 15
Рассел Крейг (Russel Craig) – старший инженерпрограммист в Unity Technologies. На момент написания книги имеет 10-летний опыт профессионального моделирования на Unity в таких областях, как разработка приложений, аппаратное и микропрограммное обеспечение продукта, моделирование сенсорных систем, симуляция обучения в медицине и разработка AR/VR. Спикер конференций Unite и мастер на все руки в Unity. Свободное время проводит с женой и детьми, увлекается сборкой компьютеров, автомобилей и просто играет в видеоигры. Я хотел бы поблагодарить мою семью, друзей и коллег за то, что они мирятся с моим напряженным графиком.
Райан Станкел (Ryan Stunkel) – профессиональный звуковой дизайнер видеоигр, руководитель собственной студии-подрядчика Blipsounds в Остине, штат Техас. Помимо Blipsounds, Райан является учителем и наставником сообщества звукорежиссеров на YouTube-канале Blipsounds. Путешествуя по миру, делился своими знаниями о звуковом дизайне видеоигр для компаний Google, PAX и Schools.
О рецензентах Гиртс Кестерис (Ģirts Ķesteris) – руководитель студии @HyperVR Games, а также внештатный инженер-программист XR/Unity в Ubiquity Inc. Лектор по разработке интерактивных 3D-сред (включая игровой движок Unity) в Vidzemes Augstskola (Видземский университет прикладных наук) (по совместительству) и независимый разработчик игр в NYAARGH! (частный предприниматель). Имеет большой опыт работы с Unity в нескольких компаниях. Спасибо моей жене за терпение и поддержку! Монтика «Тика» Сансук (Montica «Tica» Sansook) – серийный предприниматель азиатско-американского происхождения, спикер, художник по играм и блогер. Тика является соучредителем Defy Esports Bar, места проведения ночных киберспортивных клубов, и Defy Games, независимой студии по разработке игр. Она дважды окончила университет Full Sail со степенью магистра наук в области индустрии развлечений и степенью бакалавра наук в области игрового искусства. Ее обширный и разнообразный карьерный опыт варьировался от креативного развития бренда и бизнеса в области игр до управления сообществом блогеров и профессиональных киберспортивных организаций. Тика получает огромную радость и удовлетворение от поддержки других посредством наставничества, продвижения разнообразия и консультирования. Ей нравится мотивировать людей к раскрытию их потенциала и подчеркивать достижения своих коллег. В своем жизненном пути она хочет оказать положительное влияние на развитие экосистемы игровой индустрии в лучшую сторону.
Введение Книга представляет собой детальный обзор дизайна и разработки трехмерной игры-головоломки в Unity. Мы займемся дизайном, созданием и реализацией персонажей, окружения, пользовательского интерфейса, звука и игровой механики.
Кому предназначена книга? В первую очередь данная книга для тех, кто заинтересован в создании 3D-игр, но еще не начал свой путь. Мы изучим все – от самой базы до продвинутых техник. Также мы поможем всем, кто уже начал свой путь и желает изучить новые для себя аспекты разработки игр, поскольку в книге мы охватываем широкий спектр навыков и знаний.
О чем эта книга Часть I – Планирование и проектирование Глава 1 «Введение в трехмерное пространство» знакомит с трехмерной терминологией и начальным жаргоном того, через что будет проходить книга. Глава 2 «Дизайн и прототип» познакомит пользователя с кроличьей норой дизайна и заканчивается установкой Unity для создания вашего первого проекта. Глава 3 «Программирование» закладывает основы программирования. Эта глава опирается на мощь C# (C Sharp), объясняя основы логики и первоначальное использование Visual Studio.
Часть II – Сборка В главе 4 «Персонажи» рассматривается проектирование 3D-персонажей, а также обдумывается, как они будут использоваться для риггинга и анимации. Глава 5 «Окружающая среда» проведет вас через размышления об окружающей среде для вашей игры, а также о том, что мы сделали для ее проектирования и создания.
18 Введение Глава 6 «Взаимодействия и механика» посвящена тому, как следует думать о механике и что представляет собой взаимодействие для пользователя, а также охватывает программирование, необходимое для взаимодействия в нашем проекте. Глава 7 «Взаимодействие Rigidbody и физики» добавляет сложности к взаимодействию с физикой и более сложные концепции программирования. В главе 8 «Пользовательский интерфейс и меню» рассказывается о компоненте холста Unity и о том, как разрабатывается общий игровой интерфейс в любом проекте.
Часть III – Полировка и уточнение В главе 9 «Визуальные эффекты» рассказывается, как можно работать с системами визуальных эффектов, чтобы создать дополнительную эмоциональную связь с вашим миром. Это делается путем объяснения основ рендеринга и окружающих его систем. Глава 10 «Звуковые эффекты» рассказывает о звуковых системах в Unity, а также закладывает прочную основу звукового дизайна. В главе 11 «Сборка и тестирование» рассказывается, как Unity создает окончательный исполняемый файл игры, и объясняются методы тестирования для устранения ошибок, которые можно исправить с целью сделать продукт лучше. Глава 12 «Последние штрихи» выглядит как набор полезных инструментов для того, чтобы сделать вашу игру настолько идеальной, насколько это возможно. Там мы полируем наш проект, что включает в себя специальные системы частиц, освещение, доработка арта и улучшенную звуковую полировку. Дополнительная глава «Бонус: другие инструменты Unity» — это глава, в которой рассматриваются некоторые сервисы, которые Unity может предложить на тот случай, если данная книга вдохновит вас на работу над проектом, который мы не смогли охватить, например над многопользовательской игрой или смешанной реальностью.
Чтобы получить максимальную отдачу от этой книги Смотрите на эту книгу не как на учебник, а как на множество в одном месте инструментов, используемых для разработки 3D-игры. Мы рассмотрим только несколько простых примеров. Избавьтесь от логики при чтении, чтобы применить ее к своим проектам в максимально возможной степени. Будьте готовы делать собственные заметки по рассматриваемым темам. Мы немного усложняем программирование в части физики. Задавайте вопросы в канале Discord, который прикреплен через QR-код.
Загрузите файлы примеров кода Набор кодов для книги размещен на GitHub по адресу https://github.com/ PacktPublishing/Unity-3D-Game-Development. У нас также есть другие наборы из нашего богатого каталога книг и видео, доступных по адресу https://github.com/ PacktPublishing/. Загляните!
Введение 19
Загрузите цветные изображения Мы также предоставляем PDF-файл с цветными изображениями скриншотов и диаграмм, использованных в этой книге. Вы можете скачать его здесь: https:// static.packt-cdn.com/downloads/9781801076142_ColorImages.pdf.
Организация текста В этой книге используется ряд текстовых соглашений. CodeInText: обозначает слова из кода в тексте, имена таблиц базы данных, имена папок, имена файлов, расширения файлов, пути, пустые URL-адреса, пользовательский ввод и дескрипторы Twitter. Например: «Смонтируйте загруженный файл образа диска WebStorm-10*.dmg как другой диск в вашей системе». Блок кода устанавливается следующим образом: void OnStartGameButtonPressed() { SetPlayerEnabled(true); Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; this.gameObject.SetActive(false); }
Полужирный: обозначает новый термин, важное слово или слова, которые вы видите на экране. Например, слова в меню или диалоговых окнах также появляются в тексте таким образом, допустим: «Выберите System info в панели Administration».
Предупреждения или важные примечания выглядят так.
Советы и рекомендации выглядят следующим образом.
Связь с нами Отзывы наших читателей всегда приветствуются. Общий отзыв: напишите на почту [email protected] и укажите название книги в теме сообщения. Если у вас есть вопросы по какому-либо аспекту этой книги, пожалуйста, напишите нам по адресу [email protected]. Исправления: хотя мы приложили все усилия, чтобы обеспечить точность нашего контента, ошибки случаются. Если вы нашли ошибку в этой книге,
20 Введение мы будем признательны, если вы сообщите нам об этом. Пожалуйста, посетите http://www.packtpub.com/submit-errata, нажмите Submit Errata и заполните форму. Пиратство: если вы столкнетесь с незаконными копиями наших работ в любой форме в интернете, мы будем признательны, если вы сообщите нам адрес или название веб-сайта. Пожалуйста, свяжитесь с нами по адресу [email protected] со ссылкой на материал.
1 Введение в трехмерное пространство Добро пожаловать! Мы рады, что вы присоединились к нам в этом путешествии, чтобы изучить основы разработки 3D-игр. Для начала мы познакомим вас с командой, написавшей эту книгу. Трэвис Бапист (Travis Bapiste) (3D-художник) руководил оформлением, смоделировал каждую модель в игре, сделал риг персонажа и помог определить дизайн истории. Рассел Крейг (Russel Craig) (старший инженер по программному обеспечению) создал скрипты для механик игры. Райан Станкел (Ryan Stunkel) (звуковой дизайнер) создал и реализовал все звуки в проекте. Энтони Дэвис (Anthony Davis) (старший технический художник) написал книгу, руководил проектом, создавал эффекты и шейдеры и полировал наш проект. Мы извлекли лучшее из нашего коллективного опыта за более чем 50 лет (четыре мозга за каждой страницей этой книги), каждый день превращался в американские горки (слишком много веселья!). На написание книги потребовалось более шести месяцев и две редакции всей книги (а также сотни GIF-файлов, которыми мы обменялись в процессе), чтобы включить наиболее подходящие практические примеры, объясняющие новые концепции и, что наиболее важно, предлагающие подход к обучению, который работает. В конце концов, мы считаем, что нам удалось создать книгу, которая определила бы траекторию нашей карьеры в разработке игр и продвинула нас вперед как минимум на 3–5 лет. Наша книга снабдит вас всеми инструментами, которые понадобятся, чтобы начать создавать игры; однако, чтобы превратить идеи в творения, вам может понадобиться дополнительная поддержка и совет на пути. На данном моменте наш сервер Discord вступает в игру. Он вводит элемент интерактивности, чтобы мы могли общаться, вместе читать книгу и обсуждать ваши проекты 3D-игр. На нашем Discord-канале мы доступны почти всегда,
22 Введение в трехмерное пространство чтобы помочь вам с легкостью пройти книгу, поэтому, пожалуйста, не стесняйтесь заходить, чтобы поздороваться и задать любые вопросы! Не забудьте кратко написать о себе в разделе #introduce-yourself, когда присоединитесь: https://packt.link/unity3dgamedev.
Ну что же, давайте начнем!
Цель книги Наша цель – дать каждому читателю возможность сформировать правильное мышление о 3D-играх, а затем показать все шаги, которые мы предприняли, чтобы вы смогли создать свои собственные проекты. Абсолютный новичок может работать с этой книгой, однако чем дальше, тем сложнее вам могут показаться некоторые темы. Даже если это случится, то это лишь шаги к мастерству в разработке игр. Основная целевая аудитория этой книги – те, кто уже имеет некоторые знания в области разработки игр, хотя, независимо от вашего опыта, мы надеемся создать для вас увлекательное путешествие по обучению. Концепции с персонажами, программированием, шаблонами проектирования и многим другим, которые мы рассмотрим, будут усложняться. Чтобы извлечь максимальное количество пользы от книги, рекомендуем вам следующий подход. Прочитав главу, сделайте паузу, чтобы тщательно обдумать прочитанное. Если что-то оказалось непонятным, взгляните на наши проекты в GitHub, потому что практический взгляд может ответить на возникшие вопросы. В случае, если все равно останутся сомнения, то, как вариант, можно воспользоваться поиском в интернете. Если вдруг произошли какие-то ошибки в проекте, отправьте мне сообщение в Discord или обратитесь за помощью к коллегам на сервере сообщества – ссылка опубликована выше. Возможно, освежить взгляд, пропустив камень преткновения и пройти дальше, а затем снова к нему вернуться, также поможет вам. Данный подход поможет вам разобраться с подводными камнями на пути; как только вы почувствовали себя в тупике, обратитесь за помощью к коллегам. Проблемы, с которыми вы, возможно, столкнетесь, обязательно у кого-то уже были и еще будут. Их решения и обсуждение в нашем Discord укрепляют общие знания всего сообщества на любых уровнях. Концепция этой книги создана для данного подхода, соответственно, в первую очередь нужно понять, почему мы сделали так, как мы сделали. Нам потребуется некоторое время, чтобы изучить основы интерфейса Unity, но в дополнение будет полезным воспользоваться онлайн-ресурсами по обучению, их очень много.
Основы 3D 23 Обратите внимание, что вы не найдете здесь детализированной информации о том, как моделировать персонажей, риггить или анимировать их. Мы очень мало будем говорить об этом процессе, поэтому это пойдет на вашу самостоятельную тренировку. Мы расскажем, почему создали нашего персонажа именно так, как мы это сделали, с целью помочь вам научится делать что-то похожее. В проекте есть все уже готовые анимации и синематики, поэтому финальные продукты доступны для ознакомления вместе с результатами нашей работы. Такой подход – отличный способ учиться, и мы научим вас, почему все делается именно так, а не иначе. Таким образом, вы увидите конечный результат, но также будет прекрасно, если вы проявите творческий подход и придумаете собственные идеи для дизайна. И не бойтесь самостоятельно экспериментировать в новых для вас софтах по ходу чтения, в этом нет никаких ограничений. Наконец, прежде чем погрузиться в то, ради чего мы здесь собрались, мы хотели бы посоветовать вам открыть репозиторий GitHub, перейти в папку Builds и поиграть в игру для себя. Это поможет вам увидеть то, что наша небольшая команда собрала воедино в конечный результат. Поиграв в нее сейчас, вы сможете визуализировать то, через что мы прошли, соответственно, будет легче представлять результат во время чтения. Темы, в которые мы погрузимся в этой главе: основы 3D, основные концепции Unity, интерфейс Unity. Начнем со знакомства с основными компонентами разработки 3D-игр.
Основы 3D В этом разделе мы рассмотрим базовое понимание работы с 3D – от систем координат до визуализации 3D-модели, чтобы вы ясно понимали основы перед продвижением к сложным разделам. Здесь вы получите четкое представление о том, как Unity отображает элементы.
Система координат В каждом 3D-софте своя система координат. Unity имеет левостороннюю систему координат, в которой +y направлен вверх. На рис. 1.1 представлено сравнение левосторонней и правосторонней систем. Работая в данных системах, вы будете видеть положение объекта относительно трех значений: (0, 100, 0). Так мы обозначили (x, y, z) соответственно. Применять такой синтаксис будет полезным, поскольку в программировании используется похожий синтаксис положения в скриптах, как в примере. Как правило, говоря о положении, имеется ввиду transform (преобразование) внутри любого используемого Создателя цифрового контента (Digital Content Creator – DCC). В Unity под преобразованием (transform) предполагается положение (position), поворот (rotation) и масштаб (scale) объектов.
24 Введение в трехмерное пространство
Левосторонняя
Правосторонняя
Рис. 1.1. Сравнение систем координат
Таким образом, мы выяснили, что (x, y, z) – это мировые координаты с начальным положением 0, соответственно, (0, 0, 0). Ниже на рис. 1.2 мы видим общую точку всех трех направлений, которая имеет координаты (0, 0, 0) в мире. Куб имеет собственный transform, включающий в себя положение, вращение и масштабирование. Обратите внимание, что в первую очередь transform обозначает локальное положение, поворот и масштаб объекта, а мировые transform рассчитываются исходя из этого в соответствии с их иерархией.
Рис. 1.2. Система координат в 3D
Куб на рис. 1.2 находится в точках (1, 1.5, 2) мирового пространства, поскольку transform элемента представлен через мировые координаты относительно (0, 0, 0). Теперь, когда мы знаем, что преобразование куба происходит от мирового (0, 0, 0), рассмотрим взаимосвязь родитель–потомок (parent-child), наглядно описывающую локальное пространство. На рис. 1.3 сфера является потомком
Основы 3D 25 куба. Локальное положение сферы (0, 1, 0) по отношению к кубу. Если вы начнете перемещать куб, то сфера последует за ним, поскольку она всего лишь смещена от куба, а ее преобразования останутся (0, 1, 0) по отношению к кубу.
Рис. 1.3. Локальное и мировое пространство
Векторы Вектор – это единица, которая имеет более одного элемента с направлением. Далее Vector3 будет выглядеть очень похоже на то, с чем мы только что работали. (0, 0, 0) – это Vector3! Векторы используются в очень многих решениях для игровых элементов и логики. Обычно разработчик нормализует векторы таким образом, что величина всегда будет равна 1. Это позволяет разработчику очень легко работать с данными, так как 0 – это начало, 0.5 – это половина пути, а 1 – это конец вектора.
Камеры Камеры – невероятно важные компоненты! Они смиренно показывают наш взгляд на объекты, что позволяет игрокам испытать то, что мы желаем им передать. Как вы уже могли подумать, у камеры тоже есть свой transform, как и у всех игровых объектов (которые мы изучим позже в этой главе) в иерархии. Камеры также имеют несколько параметров, которые можно изменять для получения различных визуальных эффектов. В разных игровых элементах и жанрах камеры используются по-разному. Например, в игре Resident Evil используются статичные камеры, чтобы создать ощущение напряжения, и игрок не знает, что находится за окном или за углом, в то время как Tomb Raider приближает камеру, пока игровой персонаж Лара проходит через пещеры, создавая ощущение близости и эмоциональное понимание ее неудобства в ограниченном пространстве. Камеры необходимы для передачи определенного опыта, который вы будете создавать для своих игроков. Будет важным уделить этому время и изучить композиционные концепции, чтобы передать максимальные ощущения игрокам.
26 Введение в трехмерное пространство
Faces, edges, vertices, meshes 3D-объекты состоят из нескольких частей, как показано на рис. 1.4. Вершины (vertices), обозначенные зеленым цветом, являются точками объекта в пространстве относительно мира (0, 0, 0). Каждый объект имеет некоторое количество вершин и соответствующих им связей.
Рис. 1.4. Вершины, ребра, грани и меши
Две соединенные вершины образуют ребро (edge), обозначенное красной линией. Когда три или четыре ребра соединяются, образуя треугольник или четырехугольник, мы получаем грань (face). Если один четырехугольник не соединен с другими гранями, его называют плейн (plane). Соединив все три части вместе, мы получаем меш (mesh).
Материалы, текстуры и шейдеры Теперь, когда вы знаете, из чего состоит меш во всех инструментах DCC, давайте посмотрим, как это отображает конкретно Unity. На базовом уровне находится шейдер. Шейдеры можно рассматривать как небольшие программы, которые имеют собственный язык и работают на графическом процессоре, поэтому Unity может отображать объекты сцены на вашем экране. Вы можете думать о шейдере как о большом шаблоне для создаваемых материалов. Следующий уровень – материалы. Материал – это набор атрибутов, которыми манипулирует шейдер, с помощью чего мы показываем, как выглядит объект. Каждый конвейер рендеринга будет иметь отдельные шейдеры: Встроенный конвейер рендеринга (Built-in), Универсальный конвейер рендеринга (URP – Universal Rendering Pipeline) или Конвейер рендеринга высокого разрешения (HDRP – High Definition Rendering Pipeline). В этой книге мы используем второй, самый используемый вариант: URP. На рис. 1.5 показан пример материала с использованием шейдера URP Standard Lit. Это позволяет нам управлять параметрами поверхности, входными данными для этой поверхности и некоторыми дополнительными параметрами.
Основы 3D 27 А пока давайте просто поговорим о Base Map, первом элементе в разделе Surface Inputs. Термин Base Map используется здесь как комбинация Diffuse/Albedo и Tint. Diffuse/Albedo используется для определения базового цвета (красное выделение), который будет применяться к поверхности, – в нашем случае белого.
Рис. 1.5. Базовые параметры материалов
Если вы поместили текстуру на эту карту, либо перетащив текстуру на квадрат (зеленое выделение) слева от базовой карты, либо щелкнув по кружку (синее выделение) между полем и названием, вы сможете подкрасить поверхность цветом, если нужны какие-либо корректировки. На рис. 1.6 показан простой пример того, как будет выглядеть куб с оттенком, текстурой и той же самой текстурой с измененным оттенком. По мере прохождения книги мы будем открывать все больше и больше функций материалов, шейдеров и текстур. Без текстуры
С текстурой
Цветная текстура
Без оттенка
С оттенком
Рис. 1.6. Оттенок и текстура основного цвета
28 Введение в трехмерное пространство Текстуры могут обеспечить невероятную детализацию вашей 3D-модели. При создании текстуры важным фактором является разрешение. Первая часть раздела разрешения, которую необходимо понять, – это размер «power of 2» (кратное двум), например: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096… Эти числа представляют размер пикселя как по ширине, так и по высоте. Если вам понадобится использовать разные размеры для ширины и высоты, то только если числа будут кратны двум. Примеры: 256×256, 1024×1024, 256×1024 (редко встречается в использовании, но все равно корректен). Второй пункт относительно разрешения – это размерность. Самый простой способ – подумать о том, насколько большим будет 3D-объект на вашем экране. Если у вас разрешение экрана 1920×1080, то это 1920 пикселей в ширину и 1080 пикселей в высоту. Если рассматриваемый объект будет занимать только 10 % экрана и его вряд ли можно будет близко рассмотреть, вы можете использовать текстуру 256×256. Другой вариант – если вы делаете игру, в которой важны эмоции и выражение лица, может понадобиться текстура 4096×4096 или 4K только на лице во время каких-либо кат-сцен.
Физика Rigidbody Unity предполагает, что для физики нам не нужно вычислять каждый кадр игрового объекта. Для моделирования физики Unity использует движок Nvidia PhysX. Чтобы получить какие-либо рассчитанные физические отклики, в игровой объект необходимо добавить компонент Rigidbody (твердое тело). Как только вы добавили компонент Rigidbody в игровой объект, появляется меню с некоторыми возможностями воздействия на данный игровой объект.
Рис. 1.7. Rigidbody
В Unity одна единица массы равна 1 кг. Это влияет на физические решения при коллизиях. Параметр Drag добавляет трение, уменьшая скорость относительно времени. В Angular drag аналогично, но ограничено только скоростью
Интерфейс Unity 29 вращения. Use Gravity включает или выключает гравитацию, равную стандартной земной гравитации (0, –9.81, 0), поэтому масса крайне важна! А если вам не нужна конкретно земная гравитация, можете изменить настройки физики, чтобы сделать гравитацию такой, какой хотите. Подробное объяснение темы Rigidbody будет рассмотрено в главе 7. Там мы будем использовать Rigidbody при создании персонажей, а также окружающей среды и интерактивного игрового процесса.
Обнаружение столкновений Игровой объект с Rigidbody без каких-либо коллайдеров не будет полностью использовать физику и с включенной гравитацией просто провалится сквозь мир. Существует довольно много типов коллайдеров, с которыми можно поиграть, чтобы удовлетворить потребностям ваших игр. На рис. 1.8 вы можете видеть, что для 2D существуют отдельные коллайдеры. Они используют систему физики, отличную от 3D. Если вы делаете игру в 2D, обязательно используйте 2D-коллайдеры.
Рис. 1.8. Вариативность коллайдеров
Вы также можете добавить сразу несколько коллайдеров – с основными параметрами, показанными на рис. 1.8, – к объекту, чтобы он наилучшим образом соответствовал форме игрового объекта. Очень часто можно увидеть коллайдеры на пустых игровых объектах, которые находятся в связи объект– ребенок у основного объекта-родителя, чтобы упростить преобразование коллайдеров. Мы рассмотрим это на практике в главе 4 «Персонажи» и в главе 5 «Окружающая среда».
Интерфейс Unity Интерфейс Unity разделен на несколько основных компонентов. На рис. 1.9 мы видим окно Сцены (Scene) (красный цвет) и элементы в ее интерфейсе, а также меню управления свойствами инспектор (Inspector) (оранжевый цвет). Затем мы перейдем к элементам, которые неактивны в сцене, но доступны для добав-
30 Введение в трехмерное пространство ления в Окне проекта (Project Window) (фиолетовый цвет). Наконец, рассмотрим окно Игра (Game) (зеленый) и Менеджер пакетов (отдельно от рис. 1.9).
Hierarchy
Scene
Game
Project Window
Inspector
Рис. 1.9. Общий интерфейс
Окно сцены и иерархия Окно сцены и иерархия работают в тандеме. Иерархия – это то, как сцена будет отображаться во время игры. Окно сцены позволяет вам манипулировать игровыми объектами и их значениями в режиме реального времени. Кроме того, когда редактор находится в режиме воспроизведения, игре можно вносить изменения в игровые объекты в иерархии. Когда игровым объектом манипулируют в режиме воспроизведения, включая манипуляции вручную в окне сцены, как только вы остановите воспроизведение, все игровые объекты автоматически вернутся в исходное положение до запуска. На первый взгляд вам может показаться, что на рисунке слишком много информации. Слева в иерархии видно, что в сцене есть объекты. Все эти объекты имеют transform, который помещает их в мир. Если дважды щелкнуть по объекту или же сначала щелкнуть, навести указатель мыши на окно сцены, а затем нажать клавишу f, вы сфокусируетесь на этом объекте, что поместит его в центр окна просмотра сцены (viewport). После выбора объекта вы увидите его точку пивот (pivot) с цветными стрелками, по стандарту он находится в центре объекта. Данный инструмент позволяет позиционировать игровой объект в пространстве. Вы также можете расположить объект на плоскости, выбрав маленький квадрат между двумя осями.
Интерфейс Unity 31 В правом верхнем углу рис. 1.10 вы увидите гизмо камеры. Эта маленькая штуковина позволит вам легко сориентировать камеру вьюпорта на вид спереди, сбоку, сверху, снизу или изменить ее на изометрическую или перспективную камеру одним щелчком мыши. Теперь, когда вы видите объект в сцене, выбранный щелчком левой кнопки мыши в сцене или в иерархии, вы можете изменить некоторые свойства или добавить компоненты к этому игровому объекту. Тут в игру вступает инспектор.
Рис. 1.10. Сцена и иерархия
Inspector Чтобы манипулировать игровым объектом, когда вы выбираете его в сцене или иерархии, инспектор будет меняться, чтобы показать вам возможные варианты изменения для каждого объекта в отдельности. Окно инспектора на рис. 1.11 показывает некоторое количество параметров для манипуляции с объектом. Вверху имя Cube, а синий куб слева обозначает префабный (шаблонный) тип данных. Вы можете внести изменения в сам префаб, нажав кнопку Open чуть ниже имени. Это создаст новый вид сцены, который показывает только префаб. Когда вы вносите изменения в префаб, эти изменения будут дублироваться во все экземпляры данного префаба в любой сцене, которая на него ссылается. Компонент Transform показывает положение, поворот и масштаб префаба в сцене. Mesh filter показывает вершины, ребра и плоскости, из которых состоит полигон. Ниже находится Mesh renderer. Этот компонент позволит визуализировать меш, отображаемый в компоненте Mesh filter. Здесь мы можем задать материал и другие параметры, относящиеся к конкретному освещению и датчикам этого предмета, которые мы рассмотрим в главе 12 «Последние штрихи».
32 Введение в трехмерное пространство
Рис. 1.11. Окно Inspector
Далее по списку мы видим Collider и Rigidbody. Они работают в тандеме и помогают объекту реагировать на физику в реальном времени в соответствии с настройками данных компонентов. Мы много говорили об элементах сцены и их свойствах, но где они размещаются, находясь за пределами сцены? Окно Project ответит на этот вопрос.
Project window Здесь вы найдете ассеты, которые будут добавлены в сцену или использованы в качестве составного элемента для полной реализации создаваемой вами игры. Это окно является физическим представлением игровых объектов, на которые вы ссылаетесь. Все объекты в папке ассетов, показанной на рис. 1.12, физически находятся на вашем жестком диске. Unity создает метафайлы, содержащие все свойства объектов.
Интерфейс Unity 33
Рис. 1.12. Окно Project
Благодаря наличию первоначальных файлов в окне Project вы можете изменять объекты, а когда вы выбираете проект Unity (нажмите на программу Unity), все метафайлы перенастроятся и объекты в сцене перезагрузятся. Мы посмотрели на игровые объекты в сцене, разместили их, манипулируя преобразованиями, и знаем, откуда на них ссылаются. Теперь мы должны посмотреть на окно Game, чтобы узнать, как выглядит сама игра.
Окно Game Окно игры похоже на окно сцены; однако он следует правилам, встроенным в представление сцены. Игра будет автоматически рендерить содержимое сцены через основную камеру или же через ту, которую вы укажете.
Рис. 1.13. Окно Game
34 Введение в трехмерное пространство Здесь мы видим сходство с окном сцены, но в верхней части отличаются параметры. В левом верхнем углу есть раскрывающийся список Display, в котором можно менять камеры, если в сцене их несколько. Правее находится соотношение сторон экрана для настройки таргета на определенные объекты. Далее Scale, с помощью которого можно, к примеру, быстро увеличить окно или приблизить для удобства отладки. Maximize On Play развернет экран во время проигрывания, чтобы использовать все преимущества полноэкранного режима. Mute Audio отключает звук в игре. Stats дает небольшой обзор статистики в игровом окне. Позже в нашем проекте во время оптимизации мы проведем профилирование, чтобы более подробно рассмотреть, что может вызывать проблемы в игровом процессе с точки зрения использования памяти и других возможностей оптимизации.
Рис. 1.14. Статистика игры
Продолжая движение направо, вы увидите Gizmos. Это набор элементов, которые отображаются в окне игры. В этом меню вы можете отключить или включить их в зависимости от ваших потребностей.
Package Manager В вашем Unity ID будут храниться пакеты, которые вы купили в Unity Asset Store, а также пакеты, которые могут быть у вас на жестком диске или на GitHub! С помощью менеджера вы можете импортировать пакеты в свой проект. Чтобы найти менеджер пакетов, пройдите Windows > Package Manager, как показано на рис.1.15. После того как вы откроете менеджер, сначала вам будет показано, какие пакеты уже есть в проекте. Вы можете изменить раскрывающийся список в верхнем левом углу, чтобы увидеть, что является стандартным в Unity или какие пакеты вы купили в Unity Asset Store.
Интерфейс Unity 35
Рис. 1.15. Путь в Package Manager
Рис. 1.16. Package Manager
Выбрав Unity Registry, вы увидите список проверенных пакетов Unity, которые предоставляются бесплатно и являются базовой частью платформы Unity. Выбрав пакет, можете прочитать о нем документацию, представленную по ссылке View documentation.
36 Введение в трехмерное пространство Если вы выберете список In Project, он покажет вам, какие пакеты уже установлены в текущем загруженном проекте. Так вы сможете удалить ненужные пакеты в вашем проекте. My Assets – это ассеты, которые вы купили для проекта, а также те, которые связаны с вашим Unity ID. Built-in стандартен для любого проекта. Возможно, в одном из проектов потребуется включить или отключить какой-либо встроенный пакет в зависимости от ваших потребностей. Изучите их и отключите то, что не нужно; компактный проект в начале приводит к меньшей оптимизации в будущем.
Основные концепции Unity Мы уже рассмотрели, где используются некоторые концепции, а теперь более подробно раскроем их. В Unity реализован очень модульный подход к элементам, размещенным в среде разработки игр.
Ассеты Unity рассматривает каждый файл как ассет – все, включая 3D-модель, файл текстуры, спрайт, систему частиц (партиклов) и т. д. В проекте у вас будет папка Assets в качестве базовой папки для размещения всех элементов. Это могут быть текстуры, 3D-модели, системы частиц, материалы, шейдеры, анимация, спрайты и т. д. По мере того как мы увеличиваем количество элементов проекта, наша папка должна быть организована и готова к росту. Настоятельно рекомендуется поддерживать организованную структуру папок, чтобы вы или ваша команда не тратили время впустую, пытаясь найти тот единственный элемент текстуры, который был случайно оставлен в случайной папке.
Сцены Сцена содержит всю логику игрового процесса, игровые объекты, синематики и все остальное, на что ваша игра будет ссылаться для рендеринга или взаимодействия. Сцены также используются для разделения игрового процесса на части с целью сократить время загрузки. Представьте, что при каждом включении современной игры вы пытаетесь загружать абсолютно все ассеты. Это отнимет слишком много драгоценного игрового времени.
Игровые объекты Большинство ассетов, которые представлены в сцене, являются игровыми объектами (GameObject, далее GO). В некоторых случаях ассет может быть лишь компонентом GO. Один общий фактор, который вы увидите во всех GO, заключается в том, что у них есть компонент Transform. Как мы читали в начале главы, transform содержит локальное положение, поворот и масштаб. Мировые преобразования рассчитываются исходя из локальных в соответствии с их иерархией. GO может иметь длинный список связанных для функциональности компонентов или данных, которые будут использоваться в скриптах для развития механики.
Основные концепции Unity 37
Компоненты GO могут содержать несколько функциональных частей в виде компонентов. Каждый компонент имеет свои уникальные свойства. Весь список компонентов, которые вы можете добавить, довольно обширен, как можно видеть на рис. 1.17.
Рис. 1.17. Список компонентов
Каждый из этих разделов имеет свои подразделы. В книге мы рассмотрим большую часть. Когда вы добавляете ассет в иерархию сцены, для которого требуются компоненты, Unity добавит их по умолчанию. Например, когда вы перетаскиваете 3D-меш в иерархию, к GO автоматически прикрепляется компонент отображения сетки.
Скрипты Одним из компонентов, который часто используется в GO, является скрипт. В скриптах будет встроена вся логика и механика игровых объектов. Если вы хотите изменить цвет, прыгнуть, изменить время суток или собрать предмет, вам нужно будет добавить эту логику в скрипт объекта. В Unity основным языком является C# (произносится как «C sharp»). Это строго типизированный язык программирования, а это означает, что любой переменной, с которой манипулируют, должен быть присвоен тип.
38 Введение в трехмерное пространство Мы с вами будем использовать скрипты множеством способов, и я уверен, что вы уже были бы рады приступить к кодированию, но перед этим нам еще необходимо разобраться в других стандартных процессах Unity.
Префабы Используя модульную и крайне объектно ориентированную природу Unity, мы можем собрать группу элементов со значениями по умолчанию, установленными для их компонентов, которые могут быть добавлены в сцену в любое время и содержать свои собственные значения. Чтобы создать префаб, вы перетаскиваете GO из иерархии сцены в браузер ассетов. Объект в иерархии будет обозначен синим цветом по умолчанию, как показано на рис. 1.18.
Рис. 1.18. Префаб в иерархии
Пакеты Чтобы вывести модульные компоненты на совершенно новый уровень, Unity может взять пакет со всеми его зависимостями и экспортировать их для использования в других проектах. Более того, вы можете продавать свои пакеты другим разработчикам игр через Unity Asset Store. Теперь, когда у вас есть прочная основа в терминах 3D и Unity, давайте рассмотрим сам интерфейс. В следующем разделе мы изучим все наиболее распространенные элементы интерфейса Unity.
Заключение Итак, мы с вами рассмотрели несколько ключевых областей, чтобы начать свой путь в разработке игр. В этой главе мы заложили основу того, что будет происходить дальше, рассмотрев некоторые фундаментальные особенности трех основных тем. Для третьего измерения мы рассмотрели систему координат, векторы, камеры, 3D-сетки, основы физики Rigidbody и обнаружения коллизий. Этой основы, чтобы мы могли разобраться в концепциях Unity, таких как ассеты и игровые объекты основы префабов, а затем написать скрипты на C#, на данном этапе достаточно. В завершение этой главы мы прошли виртуальный тур по интерфейсу Unity – сценам, иерархии, инспекторам и диспетчеру пакетов. В следующей главе мы рассмотрим основы дизайна и прототипирования. Там вы начнете понимать, как мы описываем наши мыслительные процессы для проекта, создаваемого в этой книге. Кроме того, она заложит фундаментальные знания, которым вы будете следовать при создании собственных проектов после прочтения этой книги.
2 Дизайн и прототип Теперь, когда мы проработали весь основной жаргон разработки игр и лучше понимаем трехмерные пространства, нам нужно поговорить о самой игре. В этой книге мы создаем вертикальный срез – полнофункциональную часть игры. В данной главе мы собираемся перейти к началу работы над проектом. Основные темы включают: основы игрового дизайна, первый проект на Unity, прототипирование. Предлагаю начать с первой по списку темы и более подробно рассмотреть основы игрового дизайна. Читайте эту часть максимально вдумчиво, так как она насыщена крупицами знаний, которые выведут вашу игру на новый уровень.
Основы игрового дизайна Гейм-дизайн – молодое искусство. В любом искусстве есть несколько основных принципов, о которых нужно подумать, прежде чем приступить к изучению. Мы рассмотрим способы, которыми разработчики любят фиксировать свои мысли в «документе». Затем расскажем небольшую лекцию о том, как каждое решение должно быть преднамеренно максимально детализировано. Далее мы будем основываться на этих решениях с итерацией. Наконец, рассмотрим объяснение концепции. Начнем с обсуждения проектной документации.
Проектная документация игры Когда мы хотели, чтобы вся команда работала с новым инструментом, то делали перерывы между забегами разработки. Наш отдел управления проектами использовал стек Atlassian (Jira, Confluence и т. д.), но мы хотели сравнить, что же подойдет нам лучше всего, поэтому рассмотрели несколько разных программ. Сюда входили Hack N’ Plan, Trello, Notion и др. В итоге нам понравилась Jira за ее способность управления проектами и постановку задач, но во всем остальном мы остановились на Miro. Miro стала нашей концептуальной доской и инструментом для мозгового штурма по дизайну и рабочему процессу. Независимо от того, насколько маленькой кажется ваша игра, потребуется какая-то документация. Существуют веские организационные причины для
40 Дизайн и прототип создания документа, но самая важная заключается в том, что, когда мы что-то записываем на бумаге или обрисовываем в коллективном пространстве, мы создаем лучшее представление. Иначе это называют эвристическим дизайном. Вы можете делать это в одиночку или в команде. Некоторые дизайнеры хотят создать красиво написанный документ с четкими планами в текстовом процессоре или онлайн-инструменте для совместной работы. Так вы получите аккуратный набросок и возможность точно написать каждую деталь. Чем больше масштаб игры, тем полезнее это становится. Технические писатели, как правило, хорошо разбираются в искусстве документирования процессов. Подход заключается в том, чтобы иметь единый источник правды для любой части игры, на который любой участник команды может ссылаться при разработке, но все же такое кураторство может оказаться не лучшим методом для вас или вашей команды. Еще один вариант проектной документации игры – это программное обеспечение для коллективного мозгового штурма, благодаря чему команда может работать вместе, создавать блок-схемы, рисовать и делать наброски в творческой манере. Данный способ является прямой противоположностью кураторской формы вышеупомянутого подхода к письменному документу, но и служит другой цели. Творческая форма имеет тенденцию быть более личной и сфокусированной на искусстве. Делаются некоторые предварительные наброски концепт-арта, быстро составляются блок-схемы, чтобы выявить вопросы об элементах игрового процесса, а идеи могут быть быстро взяты и отброшены или сохранены. Такой способ проектирования не подойдет для крупной команды, поскольку в ней нет реальной организации. Новым участникам команды будет трудно адаптироваться в таких ситуациях. «Атмосфера» Макет Игры Синематик меню
Player
Player
Player
Player
CINEMATIC
CINEMATIC
Старт игры
Продвижение
Пазл 1 Открывает доступ к затерянному лесу
Пазл 2 Подходит к магическому мосту перед финальным пазлом
Пазл 3 Обнаруживает скрытую реликвию и открывает портал
Трансформация персонажа
«Портал пройден»
Основано на механике
Основано на физике
Мозаика на физике и механике
Рис. 2.1. Пример блок-схемы
Ни один из этих вариантов не является волшебной таблеткой для создания проектного документа игры, но будьте уверены, вашей команде нужен какойто документ, чтобы записывать идеи. Идеи мимолетны в уме, и некоторые из лучших идей ускользают, если их не записать. Поэкспериментируйте со своей командой, чтобы найти наиболее подходящий вариант. В проектных группах есть поговорка: «Лучший инструмент – это тот, которым ваша команда действительно будет пользоваться». Мы с вами рассмотрели два варианта проектной документации и выделили, что у них есть плюсы и минусы. Несмотря на то что моментального идеального способа не существует, отличной отправной точкой было бы начать с более ви-
Основы игрового дизайна 41 зуального и коллективного подхода. Если вы находитесь в одном помещении, это может быть маркерная доска со стикерами. Маркерная доска позволяет записывать непостоянные мысли, а стикеры – это задачи, которые необходимо выполнить. Поместите их слева в «необходимо выполнить» и переместите их на правую сторону, когда они будут завершены. Рекомендую вам уделить некоторое время на наш репозиторий GitHub, созданный для этой книги. Я добавил туда папку с изображениями GDD images, чтобы вы могли взглянуть на большое количество примеров и увидеть, над чем мы будем работать в следующих главах. https://github.com/PacktPublishing/Unity-3D-Game-Development Теперь, когда мы начали документировать дизайн игры, нам нужно собраться с мыслями, конкретизировать их и сделать осознанный выбор.
Обдуманные решения Несмотря на то что данная часть главы может быть немного короче других, отнеситесь серьезно: быть дизайнером означает создавать захватывающий мир, который имеет смысл, даже если он не имеет смысла. Игрок подсознательно делает наблюдения с невероятной скоростью. Чем больше игрок видит несоответствующих кусочков головоломки, т. е. игрового окружения или персонажа, тем больше нарушается погружение в мир. Лучший способ исправить любую проблему, нарушающую такое погружение, – это обдуманные решения. Чтобы дать очень простое объяснение этому, возьмем в пример дверные ручки. Вы видели их всю свою жизнь и использовали их интуитивно. На самом деле, когда вам приходится иметь дело с плохо спроектированной дверной ручкой, ваше «погружение» ломается. Если вы когда-либо хватались за ручку двери и пытались потянуть ее внутрь только для того, чтобы узнать, открывается ли дверь вовнутрь, то именно здесь вы столкнулись с вышеописанной проблемой. Если бы дверь была спроектирована таким образом, чтобы ее можно было перемещать только в одном направлении, правильным решением для выхода была бы индикаторная панель на месте дверной ручки, что сразу подразумевает «вовнутрь». Каждый уровень, сетка, текстура, концепция и ощущение должны быть тщательно продуманы с попыткой дальнейшей реализации. Только после того, как у вас появится веская причина разместить что-то определенным образом, не поддаваясь клише, вы сможете исследовать другие уникальные аспекты, чтобы создать что-то действительно уникальное. Проект, который вы будете создавать и использовать в этой книге, подвергся длительному и тщательному обдумыванию. Чтобы подчеркнуть это, в каждом разделе вы увидите набор вопросов, ответы на которые даны максимально подробно и лаконично.
Итеративное производство У разработки игр есть интересная потребность в том, чтобы погружение было на первом плане. Чтобы это погружение было настолько полным, насколько это возможно, команда разработчиков должна постоянно спрашивать, хорошо ли работает направление, в котором она движется. Очень часто игра, которую
42 Дизайн и прототип вы начали разрабатывать, в итоге оказывается не тем, чем планировалось. Этот цикл называется итеративным проектированием или производством. Существует множество шаблонов, которые вы можете использовать при итеративном проектировании. Подход, который будет описан здесь, – не единственный и окончательный подход к завершению проектирования, но все же хорошая отправная точка, от которой ваша команда может отходить по своему усмотрению. Итерации должны происходить часто и начинаться на ранних этапах, чтобы игра росла с точки зрения простоты ее понимания. Существует концепция под названием Минимально жизнеспособный продукт (MVP – Minimum Viable Product), согласно которой разработчики игр создают минимальное количество элементов игрового процесса, необходимое для предоставления игры тестеровщикам. Так процесс займет меньшее количество времени, и вы получите бесценную обратную связь. Когда вы передадите такой продукт тестеровщикам, вы получите обратную связь, которую вы и ваша команда, возможно, не смогли бы увидеть ввиду близости к продукту. Внимательно и непредвзято выслушайте отзывы, поскольку их опыт во время тестирования является похожим для ваших игроков во время игры. Мы работаем над намеренно разработанным опытом для как можно большего количества игроков. Эта обратная связь заставляет вас и вашу команду пересматривать дизайн и, возможно, сокращать или добавлять игровые механики для оптимизации проекта после тестирования.
Рис. 2.2. Примеры итераций в проектировании уровней
Основы игрового дизайна 43 После итераций, устраняющих основные пробелы в вашем проектировании, вы переходите к вертикальному срезу игры (о нем будет рассказано в разделе «Вертикальный срез» в этой главе). Это должна быть итерация, в которой вы знакомы с основами движения и основной игровой механикой. Ваша команда должна пройти полный игровой цикл от начала до конца с условиями выигрыша и проигрыша. Затем, как вы уже догадались, тестируйте снова, но на этот раз с новыми тестировщиками, которые никогда не видели эту игру. Задавайте похожие вопросы и некоторые новые вопросы, которые возникли во время внутреннего игрового тестирования. Цикл разработки должен казаться повторяющимся, и это: 1) продумайте и протестируйте, 2) создайте и протестируйте, 3) исправьте и протестируйте. Продолжайте использовать этот подход до тех пор, пока продукт не будет готов к выходу. Наиболее важной частью каждого шага является тестирование. Убедитесь, что отзывы тестировщиков являются убедительным указанием на то, где требуется улучшение. Мы начнем этот цикл с концептуализации.
Концепция Вам нужно сделать игру, и у вас есть группа, готовая к работе. Вы умеете преднамеренно принимать детальные решения и знаете итеративный процесс. Теперь вам нужно начать работу над концепцией. Первый шаг к запуску проекта – выяснить, какие эмоции вы и ваша команда хотите, чтобы игроки испытывали. Поскольку наш вид искусства так молод и податлив, мы можем преследовать выбранную эмоцию как угодно. В этом сила разработчика игр. Как только вы решите, какие эмоции должны получать игроки, начните думать о том, как вы можете превратить его в игровой процесс. Если эмоция – это страх, вы можете заставить игрока иметь дело с темными пространствами, используя только фонарик в качестве основного средства защиты. В таком случае вы сосредоточитесь на звуковом дизайне, поскольку видение не будет основным инструментом игрового опыта. Если эмоция – скорбь, то можно использовать повествовательный фокус, где вы играете за ребенка, потерявшего члена семьи, а игроки пройдут через повествовательный гейм-плей в мире грез. Это создает сюжетность и напряженность с четким пониманием цвета игрового мира, а также стадий горя с точки зрения ребенка. Мы могли бы продолжить обсуждение концепций, однако существует бесконечное количество сценариев. Выберите свою главную цель и работайте над ее достижением. Скорее всего, вы захотите изложить некоторые идеи на бумаге, чтобы получить представление о том, какими будут ощущения от погружения с художественной точки зрения. Потенциально это могут быть силуэты концепций персонажей или архитектурные проекты. Также это может быть коллекцией когда-то сохраненных картинок, подаривших вам ощущение эмоции, которую вы хотите вызвать и из которой можете черпать идеи.
44 Дизайн и прототип
Рис. 2.3. Концепции, используемые в проекте
В любом случае главное здесь – визуальное воплощение идей. После того как вы нарисовали несколько артов и у вас появилось визуальное представление о том, как это можно построить, мы создаем проект на Unity.
Первый проект в Unity 45
Первый проект в Unity Итак, вы собрали концепцию, которую хотите развивать, и теперь нам нужно получить сам Unity и создать в нем проект. Для этого понадобится Unity Hub, далее – выбрать версию, а затем шаблон для старта работы.
Unity Hub Unity Hub – это небольшое приложение, в котором централизованно хранятся все проекты, поэтому все ваши проекты, а также установленные вами версии Unity легко доступны. Чтобы получить доступ в Unity Hub, вам нужно перейти на сайт unity.com и создать свой UnityID. После создания учетной записи нажмите синюю кнопку Get Started. Следуйте инструкциям, которые лучше всего соответствуют вашим потребностям и операционной системе. Загрузите и установите Unity Hub и приступайте к творчеству!
Выбор версии Unity имеет несколько версий, такие как Alpha, Beta, Official и LTS-релизы. Альфа-версии имеют экспериментальные функции, которые могут быть не полностью завершенными или готовыми к работе, а также не рекомендуются для сборок, поскольку могут содержать функции, вызывающие ошибки сборки. Студии и энтузиасты могут использовать его для тестирования механики, функций движка или пакетов. Как правило, они опережают официальные релизы. Бета-версия похожа на альфа-версию; однако это экспериментальные версии самой последней официальной версии. Официальные релизы – это стабильные текущие релизы. LTS означает долгосрочную поддержку (Long-Term Support). Это окончательные выпуски версии с небольшими исправлениями, если были обнаружены ошибки. Проще всего посмотреть версии на Unity Hub.
Рис. 2.4. Примерный список версий Unity
46 Дизайн и прототип Релизы LTS рекомендуются для рабочего приложения. Если ваша команда экспериментирует или хочет создать прототип с новыми функциями, это возможно только в предварительных версиях. После выбора версии вам необходимо выбрать шаблон из параметров Unity при создании нового проекта. Эта книга, однако, не зависит от версии. Если вы приобрели эту книгу после 2022 г., она по-прежнему актуальна настолько, насколько это возможно. Может быть, пользовательский интерфейс на скриншотах будет немного отличаться, но основы останутся нетронутыми.
Выбор шаблона Unity предлагает вам несколько вариантов шаблонов, когда вы нажимаете New на вкладке проектов. Вы увидите варианты 2D, 3D, Универсальный конвейер рендеринга (URP) и Конвейер рендеринга высокой четкости (HDRP). Существуют большие различия в рендеринге, а также некоторые функции, которые могут быть необходимы вам и вашей команде для работы. Упомянутые различия между данными шаблонами возникли, когда появился Конвейер рендеринга со сценариями (Scriptable Rendering Pipeline – SRP)!
Scriptable Rendering Pipeline Рендеринг и компьютерная графика – это детальная тема, по которой вы могли бы получить докторскую степень, поэтому мы лишь поверхностно коснемся того, что возможно с помощью конвейера рендеринга. Верхний уровень конвейера включает в себя три задачи: отбрасывание, рендеринг и постобработку. В каждой из этих категорий многие задачи выполняются в определенном порядке и с определенной степенью точности. Основная функция всего этого – оптимизировать представление высокой частоты кадров для конечного пользователя, а также поддерживать художественный стиль, необходимый для того опыта, который вы хотите передать пользователю. С появлением SRP эти шаблоны разделились на три основные категории: встроенные, универсальные и высокого разрешения. Чтобы понять эти шаблоны, давайте разобьем их на соответствующие группы. Для нашего проекта мы будем использовать универсальный рендеринг, поскольку нам понадобится несколько его функций.
Встроенный рендеринг Это более старая версия рендеринга, в которой не используются конвейеры с поддержкой сценариев. Существует великое множество приложений для встроенного рендеринга. И 2D-, и 3D-шаблоны работают со встроенными системами рендеринга, так как это стандарт, для которого было создано большинство ассетов в хранилище ассетов до появления SRP. Вы можете думать о «встроенном» как о базовом опыте в Unity. Есть несколько причин, по которым вы вряд ли захотите использовать встроенный рендерер. Если вы хотите применить объемное освещение, частицы графического процессора или трассировку лучей, то для этого лучше использовать конвейеры рендеринга с поддержкой сценариев.
Прототипирование 47
Универсальный рендеринг Универсальный конвейер рендеринга назван удачно, так как он имеет большинство функций, работающих с конвейером рендеринга с поддержкой сценариев. Если вы хотите создать 2D-игру, это лучший вариант, так как он имеет встроенный пиксельный рендеринг, 2D-освещение и 2D-тени. Для варианта 3D это тоже фантастический выбор. Для URP и HDRP доступны два графика: ShaderGraph и VFXGraph. ShaderGraph – это визуальный инструмент для создания шейдеров, который позволяет визуально писать сложные шейдеры. Основная функция VFXGraph – быть системой частиц, ориентированной на частицы графического процессора, что позволяет вам создавать на экране миллионы частиц одновременно для потрясающих визуальных эффектов. В нашем проекте мы используем частицы на основе графического процессора, за обработку которых отвечает VFXGraph, а также покажем, как пользоваться ShaderGraph. С учетом требований мы решили работать с URP в качестве конвейера рендеринга. Если вы ищете более физически точную систему рендеринга с трассировкой лучей и объемными облаками, HDRP – это то, что вам нужно.
Рендеринг высокой четкости Главная цель конвейера: обеспечить наилучший результат, оставаясь при этом максимально оптимизированным. Использовать HDRP или нет – широко обсуждаемая тема. Есть несколько основных причин, по которым вам подойдет HDRP; например если вы хотите сделать физическое небо со слоями облаков, объемными облаками, несколькими направленными источниками света, настраиваемыми параметрами теней и трассировкой лучей, включая отражения с трассировкой лучей, объемные изображения и несколько выходных данных высокоуровневых шейдеров. Также есть много других высокоуровневых опций рендеринга, которые может предоставить только HDRP. Эти концепции являются глубокими темами в мире компьютерной графики, и мы просим вас ознакомиться с ними, чтобы увидеть прекрасную работу того, во что превращается рендеринг в реальном времени.
Прототипирование Теперь, когда у вас есть созданный проект, можете начать собирать ассеты для создания игры. Прототипирование может происходить множеством способов. Невозможно перечислить все способы создания прототипов каждой студией, поскольку у каждого бизнеса свой способ создания. Мы поговорим об основных паттернах прогрессии, которые распространены в отрасли в целом. Разбивая жизненный цикл любой задачи, основанной на итерации, необходимо иметь цикл для удаления нечистот. Это обычно рассматривается как анализ, проектирование, внедрение и тестирование, а затем повторение итерации до завершения. Этап прототипирования также проходит через все эти этапы. Взгляните на них все и проработайте каждую часть, которая имеет смысл для вас или группы, создающей вашу игру.
48 Дизайн и прототип
Цифровое или бумажное создание В этой форме прототипирования создатель разбивает видеоигру на этапы в физической или цифровой системе, чтобы пройти каждый игровой цикл или опыт, который игрок будет испытывать на протяжении всей игры. Иногда это может быть создание бумажной настольной игры для выполнения правил. Иногда это может включать в себя цифровое рисование игровых эскизов через пользовательский интерфейс для интуитивного понимания игрового процесса.
Grayboxing Это именно то, что вы подумали! Набор нетекстурированных фигур в сером цвете, которые выстраивают вашу среду, чтобы рассказать ее историю через силуэт. Эта версия прототипирования особенно важна, если вам нужен очень прямой угол камеры, который необходимо отобразить, и у вас нет ассетов для настройки среды. Это также может быть важно при разработке концепт-арта, когда вы передаете композицию концепт-художникам для более быстрого выполнения работ.
Рис. 2.5. Примеры Greyboxing в проекте
Прототипирование 49 На готовом «сыром» ландшафте концепт-художник может рисовать, чтобы получить визуализированную концепцию и создать больше дизайнерских идей, даже если это означает изменение окружающей среды, поскольку предоставленный сырец является быстрой имитацией пространства для начала работы. После данного этапа вполне можно приступать к работе над доказательством концепции.
Proof of Concept (PoC) Название довольно точное. Здесь вы очень тщательно подходите к своему тестированию. Например, нужно настроить камеру, чтобы получить более конкретное представление об игровом процессе. Это может занять несколько итераций, и желательно, чтобы несколько человек этим занимались, если у вас есть команда.
Рис. 2.6. Пример итерации игрового ассета
50 Дизайн и прототип На рис. 2.6 показан пример итерации некоторой архитектуры. Мы начали с простой арки, которая казалась лишь началом фантастики. После того как мы поместили ее на уровень, продумывание стиля и добавление более привлекательных деталей в арку заняло некоторое количество времени. Это полезная концепция, чтобы осознать, что ваши ассеты могут быть идеальными с самого начала. Чтобы прийти к MVP, нужно с чего-то начать и двигаться к величию!
Минимально жизнеспособный продукт (MVP) Как следует из названия, это начальная сырая версия игры. К примеру, для вашей игры в платформере нужна механика прыжков. Возможно, у вас обязательно должна быть механика качели, независимо от того, сколько у вас финансирования? На данном этапе вам не нужны отполированные художественные ассеты или даже анимация. Цель MVP – продемонстрировать, что особенности игрового процесса находятся в приемлемом диапазоне, чтобы гарантировать, что все, что построено на вершине, имеет основу механики MVP, работающей должным образом.
Вертикальный срез Иногда у вас есть хорошее представление о художественном направлении, основной механике и повествовании, но вам нужно собрать отзывы или, возможно, финансирование. Вертикальный срез – это когда вы берете очень тонкий срез (небольшую часть) игры и полируете его в готовую версию игры, чтобы создать шумиху и ощущение конечного продукта. Демоверсии аналогичны концепции вертикального среза. Это сложнее, чем MVP, поскольку здесь речь идет об уровне полировки артов, анимации, механики, освещения и т. д., чего не ожидается от MVP. На это может уйти значительное количество времени, так как должно появится понимание конечного продукта, которое вряд ли было, когда создавался MVP. Последний тип прототипирования является наилучшим вариантом для нужд нашего проекта в книге. То есть мы разрабатываем небольшую часть игры, чтобы получить четкое представление о том, какой может быть игра в целом. Это наш лучший вариант. При создании прототипов, возможно, вашей игре потребуется пройти через все это с целью получить надлежащий игровой процесс, в который приятно играть. Ну или не понадобится вовсе создавать точный результат – зависит от каждой команды разработки.
Заключение Мы рассмотрели важные темы – игровой дизайн, какие варианты выбрать для вашего первого проекта и основы прототипирования. Мы рассмотрели, как вы и ваша команда можете сотрудничать в документе Word или более наглядной блок-схеме, чтобы собрать идею игры. Суть заключается в том, чтобы воплотить идеи дизайна в реальность. Сделав это, вы должны погрузиться в свой
Заключение 51 первый проект Unity, выбрав, с каким шаблоном работать, какие использовать эффекты, соответствующие задуманной концепции вашей игры, такие как частицы графического процессора. Наконец, мы рассмотрели прототипирование, чтобы запустить часть проекта и понять, передает ли он тот самый задуманный опыт игроку. В следующей главе мы начнем рассматривать программирование, которое поможет вам воплотить в жизнь все игровые идеи.
3 Программирование Добро пожаловать в главу 3! Здесь вы узнаете обо всех основах C# и о том, как использовать его в Unity. Мы рассмотрим основную часть знаний в области программирования, необходимых для большинства проектов. К данной главе вы будете ссылаться на протяжении всей книги, когда мы приступим к написанию сценариев в каждой следующей главе. Сначала нам нужно будет убедиться, что среда вашего компьютера настроена для начала программирования с Unity, а затем перейти к основам программирования. Эта глава послужит основой для ваших проектов Unity. Темы включают: настройку среды, переменные, типы данных, логику программирования, методы.
Настройка среды Среда программирования, в частности, относится к интегрированной среде разработки (IDE – Integrated Development Environment), которую вы будете использовать, и все что с ней связано. C# является частью .NET Framework от Microsoft, которую необходимо установить на вашем компьютере для работы. К счастью для нас, многие интегрированные среды разработки в мире установят его для вас, когда вы начнете работать с C#. А что еще лучше, если Visual Studio установлена из Unity Hub, то уже имеет предварительные настройки, поэтому можно сразу приступить к разработке проекта! Давайте рассмотрим этапы настройки своей среды.
Среда Unity Microsoft Visual Studio бесплатна, напрямую подключается к Unity и поставляется с инструментами, которые сразу помогут вам! Напоминает ситуацию, когда вы чините машину и кто-то просто подает вам нужный инструмент. Есть несколько шагов для уверенности, что каждое приложение взаимодействует друг с другом. Давайте пройдемся по ним вместе и убедимся, что мы находимся на одной странице, и таким же образом будем двигаться далее по
Настройка среды 53 книге; мы сразу проработаем небольшие фрагменты кода. Не нужно проверять температуру воды, давайте сразу нырнем! 1. Введите в поисковике Unity Hub и выберите верхнюю ссылку. Вы перейдете на официальную веб-страницу Unity, где сможете скачать Unity Hub. После установки вам нужно будет установить версию Unity. Мы рекомендуем выбрать последнюю версию LTS. Если у вас не установлена Visual Studio, не переживайте, будет возможность установить ее предварительно настроенную при установке Unity. На рис. 3.1 ниже вы можете видеть, что у нас уже установлена Visual Studio. Если потребуется, рядом с названием будет флажок, вам нужно поставить его. Затем установится приложение, готовое к работе!
Рис. 3.1. Модальное окно установки Unity Hub
2. После установки нужно будет проверить, готово ли соединение между программами для их совместной работы. Подключим к нему Unity и начнем учиться. Для начала закройте Visual Studio и откройте проект Unity, который мы создали в предыдущей главе. Если вы его еще не сделали, то нет лучшего времени, чем сейчас. Перейдите к этим соответствующим меню, чтобы подключить Visual Studio к Unity: Mac: Unity (верхний левый угол экрана) -> Preferences -> External Tools, PC: Edit -> Preferences -> External Tools. Далее в раскрывающемся списке выберите Visual Studio. Перейдите в папку Assets в окне проекта и щелкните правой кнопкой мыши в сером открытом пространстве. Следуя рис. 3.2, вы можете создать сценарий, выбрав Create -> C# Script. Назовите его ScriptingLesson и дважды
54 Программирование щелкните, чтобы открыть. Теперь должно открыться Visual Studio с соответствующими блоками из Unity. Visual Studio будет считывать файл проекта, а Unity будет обновлять его при любых изменениях.
Рис. 3.2. Создание скрипта C# в редакторе
Теперь мы можем приступить к написанию скриптов! В следующих разделах мы будем работать с основами. Они останутся с вами и станут частью каждого проекта, над которым вы будете работать в будущем. Здесь можно поместить закладку и вернуться к ней для справки, когда понадобится. Прежде чем мы пойдем дальше, стоит вас предупредить. В этой и других главах может появиться место, где код станет неуправляемым. Ваш скрипт может не работать из-за разницы в одной строке или забытой точки с запятой. Это случается очень часто. Нет необходимости беспокоиться; в проекте у нас будут безопасно настроены все скрипты, чтобы вы могли легко восстановиться. Тем не менее обратите внимание, что в листингах присутствует много комментариев для упрощения чтения. Соответственно, строки в главах будут отличаться от тех, которые вы видите у себя в коде. Мы постарались по-максимуму пояснять строки, но иногда встречается лишь один комментарий. Будьте гибкими вместе с нами, поскольку комментарий может иметь значение для понимания внутренней работы всего скрипта и важнее, чем точность номера строки.
Основы Итак, установив Visual Studio и подключив его к редактору Unity, перейдем к основам. В этом разделе мы поговорим о типах данных, переменных, логике или потоке кода, методах, классах и MonoBehaviour. В этом разделе главы
Основы 55 много информации, и вы часто будете сюда возвращаться. Для удобства, если у вас есть стикер, поместите его в эту главу. Когда вы откроете файл, будет автоматически заполнен C#, который нам не понадобится для этой части. А пока удалите все лишнее, чтобы это выглядело так: using UnityEngine; public class ScriptingLesson : MonoBehaviour { // Данные и переменные // Логика и Поток // Методы }
Данный код выполняет две основные задачи. Первая строка импортирует библиотеку UnityEngine, чтобы мы могли использовать типы и методы из пространства имен UnityEngine. Это называется «директива using». Внутри пространства имен UnityEngine есть доступ ко всем нашим игровым типам, таким как GameObject. Так как мы будем работать в редакторе, чтобы манипулировать этими GameObjects, нужно использовать пространство имен в нашем классе. Следующая часть – это класс ScriptingLesson, который наследуется от MonoBehaviour. Наследование является частью объектно ориентированного программирования (Object Oriented Programming). Этот класс должен наследоваться от MonoBehaviour, так как он напрямую влияет на объекты в игре. В разделе «Логика программирования» ниже объясняется, как используется наследование от MonoBehaviour. Как вы уже могли догадаться, «//» означает комментарий. Все, что находится в этой строке, не будет компилироваться IDE. Вы можете использовать комментарии, например, чтобы помочь себе с псевдокодом или добавить некоторые определяющие слова в свой код, чтобы помочь другим программистам, которые будут работать с вашим кодом. Мы используем его в организационных целях. После внесения изменений в сценарий сохраните их, нажав Cmd + s или Ctrl + s. Если сейчас вернуться в Unity, вы увидите, что Unity скомпилировал скрипты. Каждый раз, когда мы вносим существенные изменения в скрипт, мы возвращаемся в Unity и проверяем, все ли там работает. Бывают случаи, когда редактору Unity не нравится код, с которым мы работаем, а Visual Studio не получает эти предупреждения или ошибки редактора. В Scene добавьте пустой GameObject. Назовите его ScriptingLesson и выберите его. В Inspector нажмите Add Component и введите scriptinglesson в строке поиска. Щелкните по скрипту левой кнопкой мыши, чтобы добавить его в пустой GameObject. Есть еще один способ добавить компонент. Если у вас выбран scriptinglesson, вы также можете щелкнуть и перетащить его в инспектор, тем самым добавив в раздел компонента GameObject. Оба способа применимы на практике. Если скриптов много и вы точно знаете, что хотите добавить, можно использовать Add Component и ввести имя скрипта. Теперь, когда мы будем вносить изменения, вы увидите их на GameObject. Прежде чем перейти к каким-либо типам данных, мы должны немного пояснить переменные.
56 Программирование
Переменные Как и в алгебре, переменные являются контейнерами для чего-то. C# – это строго типизированный язык программирования. Это означает, что каждая переменная должна иметь свой собственный тип данных, связанный с ней при ее объявлении. Существуют рекомендации о том, как называть ваши переменные и какой тип данных использовать в определенных ситуациях. Мы подробно расскажем об этом в каждом разделе. Соглашения о присвоении имен чувствительны к регистру, и каждый тип именования имеет свой собственный набор правил, которым необходимо следовать.
Типы данных В C# используется 10 типов данных Unity, однако в Unity нам в первую очередь нужно хорошо знать четыре из них. Это bool, int, float и string. Мы будем создавать каждый из этих типов данных в созданном нами файле ScriptingLesson.cs.
Bool Это означает тип данных Boolean, который предназначен либо для истинных (true), либо для ложных (false) переменных. Эти значения также представлены 1 (True) или 0 (False). Boolean используется, когда ваш персонаж входит в область, в которой он не должен приводить в действие что-либо, например ШИПОВУЮ ЛОВУШКУ! В строку 5 добавьте: public bool isActive;
Данная строка состоит из четырех частей, и все они имеют определенные цели: public позволяет Unity получить доступ к объекту, который мы создаем для использования в редакторе; bool – это тип данных объекта, который мы создаем; isActive – это имя данных bool, которые мы создаем. По умолчанию в качестве значения будет установлено false; точка с запятой (;) используется для обозначения конца команды. Если вы сохраните и вернетесь в редактор Unity, вы увидите, что в инспекторе появился флажок Is Active. Это должно выглядеть примерно так:
Рис. 3.3. Флажок Is Active
Int Целое число, или int, – это целое число, такое как 1, 100, 200, –234571 или 0. Оно не может содержать десятичные разряды, т. е. знаки после запятой. Применяется, например, если у вас есть дискретное значение, необходимое для прямого или обратного счета. Количество очков, набранных за игровую сессию, – отличное место для использования целых чисел.
Основы 57 В строку 6 добавьте: public int
myInt;
Очень напоминает bool. Мы объявляем, что myInt является общедоступной переменной целочисленного типа данных. После сохранения вернитесь в редактор Unity, вы увидите ввод текста справа от переменной с именем myInt. Вы не можете поставить точку (.), чтобы сделать десятичную дробь, поскольку это целое число, и в конкретном случае допускаются только целые числа.
Float Вы можете спросить, как я могу получить десятичные разряды в своих числах? Ответ – всемогущий float! Это число с плавающей точкой, с помощью чего вы можете получить десятичные разряды, такие как 1.3 или 100.454. У числа с плавающей точкой есть небольшой уникальный фактор. Когда вы пишете скрипт, необходимо поставить строчную букву f после значения, чтобы компилятор знал, что значение является числом с плавающей точкой. C# предполагает, что любое число без f в конце, такое как 3.14, является типом double. Мы не будем использовать double для нашего скрипта, поэтому важно не забыть добавить строчную f после чисел с плавающей точкой. Строка 7: public float myFloat = 3.14;
В данный момент возникла проблема с номером, и 3.14 подчеркнуто красным, верно? Если навести курсор на область, подчеркнутую красным, вы получите сообщение об ошибке. Выглядит это примерно так, как показано на рис. 3.4:
Рис. 3.4. Ошибка CS0664
Visual Studio пытается сообщить, что введенное вами число будет восприниматься как double (двойное), поэтому давайте внесем правку, чтобы помочь IDE. Правки в строке 7: public float myFloat = 3.14f;
Ну вот. Теперь у нас есть объявленный и инициализированный float. Мы объявили его как myFloat и инициализировали значение 3.14. По умолчанию число с плавающей точкой равно 0, но, когда вы указываете ему присвоить значение по мере его объявления, среда IDE перезаписывает этот 0 по умолчанию значением, которое вы для него установили. Когда вы войдете в Unity и посмотрите в инспектор, то увидите, что значение начинается с 3.14.
58 Программирование
String Все это время мы работали с числами. Теперь настало время букв. Строки содержат значения символов. Примером этого может быть отображаемое имя персонажа. Строка 8: public string mystring = "Myvari";
Выглядит похоже, но теперь вы можете вписать буквы! Здесь следует отметить интересный момент: все входные данные общедоступных значений выглядят одинаково, поэтому нам нужно убедиться, что имена наших переменных уникальны.
GameObject Довольно интересный тип данных, поскольку он уникален для Unity. GameObject – это контейнер для хранения элементов, которые пощещаются из сцены или префаба. Это чрезвычайно мощно, так как элемент, размещаемый здесь, имеет компоненты, и мы можем получить к ним доступ из скрипта, чтобы сделать очень много вещей. Строка 8: public GameObject myGameObject;
Сохраните код и вернитесь в редактор. На этот раз вы заметите, что вместо поля ввода нужно разместить GameObject. В нашей сцене есть направленный свет. Давайте перенесем сюда этот свет из иерархии. Теперь у вас есть референс на игровой объект сцены! Давайте рассмотрим логику и поток, а заодно увидим, что мы можем сделать с нашими исходными переменными.
Логика программирования Мы создали несколько удивительных переменных, заполненных фантастическими данными. Единственная проблема в том, что мы ничего не делаем с этим. Почему бы не начать прорабатывать логику в нашем маленьком скрипте, чтобы установить связь с тем, зачем нам вообще нужно программировать? Для этого мы собираемся перейти к операторам if и циклам while. Прежде чем приступить к работе со временем выполнения, нужно добавить MonoBehaviour в наш класс. Термин для действий, которые мы собираемся предпринять, – наследование (inheritance). Мы унаследуем наш класс от MonoBehaviour, что даст нам доступ к его классу! Сделать это очень просто. Вспомним строку 3: public class ScriptingLesson : MonoBehaviour
Мы правильно поступаем, добавляя : MonoBehaviour после имени класса, и теперь у нас есть доступ к любому из методов внутри класса MonoBehaviour. Есть довольно много методов, которые мы можем использовать из MonoBehaviour. Сейчас мы будем применять методы Start() и Update(), унаследованные от MonoBehaviour.
Основы 59
Операторы if Теперь следует настроить простой код для включения и выключения GameObject в соответствии с логическим значением bool isActive, которое уже определили. Проверим это на методе Update, являющемся частью MonoBehaviour. Начиная со строки 13: private void Start() { isActive = true; } private void Update() { if (myGameObject != null) { if (isActive) { myGameObject.SetActive(isActive); } else { myGameObject.SetActive(isActive); } } }
Внутри метода Start из MonoBehaviour мы устанавливаем значение true для isActive. Это сделано для того, чтобы логическое значение учитывалось независимо от того, что было установлено в редакторе. Далее метод Update. Метод Update из MonoBehaviour будет проверять весь код внутри фигурных скобок в каждом фрейме. Сначала мы проверяем определенный нами GameObject, сравнивая его с нулевым значением. Это проверка достоверности. Null – специальное ключевое слово, обозначающее отсутствие типа или данных. Если вы не выполните эти проверки, ваш редактор не сможет начать проигрывать, так как будет нулевое исключение. Например, если у вас есть общедоступный GameObject, который не назначен в инспекторе, это вызовет нулевое исключение, поскольку GameObject имеет нулевое значение! Внутри проверки достоверности у нас есть оператор if/else. В данном случае он сообщает, что если переменная isActive имеет значение true, то установите для myGameObject значение active. Для всего, кроме true, установите в myGameObject значение inactive. Если вы сохраните код и нажмете кнопку воспроизведения, то сможете выбрать игровой объект scripting lesson, а затем снимите флажок isActive. Он выключит свет. Поскольку это проверка каждого кадра, вы можете делать это вечно, и он будет включаться и выключаться, пока вам не надоест. Прежде чем мы перейдем к циклам while, следует реорганизовать крайнюю часть листинга. В нем мы изучили синтаксис if, но нужно сделать его лучше! Мы запускаем эту проверку в каждом кадре, поэтому блок if здесь не нужен, так как у нас есть значение Boolean для сравнения. Чтобы сэкономить время на вычисления, реорганизуйте код:
60 Программирование private void Update() { if (myGameObject != null) { myGameObject.SetActive(isActive); } }
Каждый кадр, если myGameObject не равен нулю, устанавливает его активное состояние либо в true, либо в false, в зависимости от того, какое значение установлено для значения Boolean. Нам не нужно спрашивать, правда это или нет, поскольку тип данных имеет только два состояния! Это круто. Давайте перейдем к циклам while и посмотрим на зацикленный код.
While Оператор if – это простой шаблон ветвления, проверяющий истинность или ложность для выполнения чего-либо. Циклы while будут постоянно запускать код до тех пор, пока оператор не станет истинным или ложным. Это может вызвать проблемы – некоторые задачи могут выполняться вечно. Это называется бесконечным циклом и может привести к зависанию вашего приложения на неопределенный срок или до тех пор, пока оно не будет принудительно закрыто. При этом бесконечные циклы обычно не вызывают особой суеты. Однако необходимо обращать внимание на критерии при создании цикла while. Начиная с 32-й строки: while (MyInt > 0) { Debug.Log($"MyInt should be less than zero. It's currently at: {MyInt}"); MyInt--; }
В этом цикле while мы делаем несколько новых вещей: журнал отладки (debug log), строковую интерполяцию (string interpolation) и декрементный счетчик (decrementer). Давайте пройдемся по ним. Debug log Debug.Log позволяет нам передать строку, и она будет записана в консоли внутри Unity. Это очень полезно, если происходят странные вещи, и вы хотите вывести информацию о времени выполнения на консоль. Строковая интерполяция Внутри журнала мы выполняем действие, называемое интерполяцией строк. Это очень удобный способ добавить ваши переменные в строку. Синтаксис начинается со значка $, за которым следуют двойные кавычки. Далее текстовая строка, содержащая пробелы. Заметьте, что внутри строки есть фигурные скобки {}! Если поместить имя переменной в фигурные скобки, вы получите данные, переданные в строку. В цикле while чуть выше вы можете увидеть, как мы выполняем это в строке Debug.
Основы 61 Декрементный счетчик Следующая строка – декрементный счетчик. Пример: MyInt = Myint – 1;
После сохранения и запуска в консоли ничего не будет. Это потому что мы не установили значение для MyInt, соответственно, по умолчанию оно равно 0. Цикл while не будет выполняться, потому что MyInt не больше 0. Внесем изменение. Строка 16: MyInt = 10;
Сохраните и запустите игру. Теперь, если вы посмотрите на консоль, она быстро уменьшит MyInt до 0 и выведет строку, сообщающую ее текущее значение. В инспекторе вы увидите, что MyInt также показывает 0. Сейчас у нас появилось представление о логике программирования, добавим же немного функциональности.
For Как и while, циклы for представляют собой итерацию по заданному количеству элементов. Циклы for чаще всего используются, если есть представление о том, сколько раз потребуется выполнить итерацию. Сделаем простой цикл for, чтобы показать его синтаксис. Для этого сделайте из строки 35 комментарий, так как журнал отладки цикла while пока не нужен. Затем в строке 39 добавьте этот блок кода: for (int i = 0; i < 10; i++) { Debug.Log($"For Loop number: {i}"); }
Именно for, как правило, используются чаще. Давайте сравним их.
For или While Циклы for и while похожи по функциям. Они предназначены для итерации некоторого набора объектов. Правила на этот счет не высечены на камне. Технически они взаимозаменяемы, но есть нюанс с читабельностью. Цикл for читается так, будто есть заданное число для итерации. Это значение не обязательно должно быть известно циклу for, примером этого является группа игровых объектов. Если бы вам нужно было выполнить итерацию и логику по всем из них, вам не нужно было бы кодировать количество объектов, поскольку группирование имеет счетчик. Вы можете итерировать этот счетчик. Подробный пример будет в главе 6 «Взаимодействия и механика». Разница между циклами заключается в том, что while читается следующим образом: сделайте что-нибудь, пока условие не станет истинным. Неважно, сколько объектов нужно повторить, так как это будет продолжаться до тех пор, пока не будет выполнено другое условие. Циклы while имеют врожденную проблему для создания бесконечных циклов. Если вы не на 100 % понимаете, что зацикливаете, или случайно используете неправильный символ (например, < вместо >) для своего условия, вы можете столкнуться с таким бесконечным ци-
62 Программирование клом. Как мы уже сказали, циклы for используются чаще, но и while занимают хорошее место в программировании.
Методы Если логика – это масло программирования, то методы будут хлебом. Цель метода – выполнять действия в сжатой форме. Очень простым примером метода является базовая функция калькулятора, называемая Add. Чтобы выполнить эту функцию, делаем три вещи: создаем несколько общедоступных переменных для добавления, создаем способ запуска метода и сам метод. Мы решили использовать систему ввода для всех наших входных данных. Чтобы это заработало, здесь также нужно добавить несколько новых концепций. Ранее мы просили вас вставлять код в определенные строки. Теперь же нужно вставить код в определенные секции. В начале кода нам нужно сообщить программе, что мы хотим использовать систему ввода. Для этого добавьте оператор using: using UnityEngine; using UnityEngine.InputSystem;
В разделе переменных класса добавьте следующие строки: public int addA; public int addB; public int totalAdd; private InputAction testInput = new InputAction("test", binding: "/b");
Мы сделали переменные int общедоступными, чтобы иметь возможность вносить в них изменения и видеть, что они могут быть изменены во время выполнения, когда мы запускаем метод. Система ввода является private, так как на нее не должны влиять никакие другие скрипты. Это хорошая практика, о которой стоит подумать при программировании кода. Если переменная не будет изменена из другого скрипта, сохраните ее как приватную переменную. Хотя это может не оказать неблагоприятного влияния на текущую рабочую среду в небольшом проекте, позже могут возникнуть конфликты, когда проекты начнут расти. Лучше сделать код чистым с самого начала. InputSystem требует, чтобы слушатели ввода были включены, а когда они не используются – отключались. Мы создадим два метода OnEnable и OnDisable для MonoBehaviour. private void OnEnable() { testInput.performed += OnTestInput; testInput.Enable(); } private void OnDisable() { testInput.performed -= OnTestInput; testInput.Disable(); }
Основы 63 Эти методы автоматически срабатывают во время выполнения. OnEnable – сразу после Awake при инициализации. В книге данные методы будем рассматривать несколько раз с разных точек зрения. Причина, по которой эти методы используются именно сейчас, заключается в том, чтобы добавить метод OnTestInput при выполнении testInput. Мы привязали букву B к нашему вводу в части переменной, теперь добавляем метод, который будет выполняться при ее нажатии. Вне метода update сделаем еще один метод добавления. private int IntAdd(int a, int b) { totalAdd = a + b; return totalAdd; }
Этот метод private, соответственно, за пределами этого класса мы не сможем получить к нему доступ. Мы задали, чтобы целое число вернулось, и его имя будет intAdd. В скобках после имени указаны аргументы метода. У нас есть два целых числа: a и b. Нам нужно определить их типы данных и их имена. Когда метод запущен, наш метод создает два целых числа со значениями на тот момент и присваивает их переменным a и b. Мы сделали totalAdd равным этим значениям, чтобы можно было показывать изменение значений в инспекторе, а также в консоли для дальнейшей отладки. Чтобы собрать все это вместе, нам нужно создать метод OnTestInput. Чтобы провести тест простым нажатием кнопки, стоит знать некоторые термины. Позже, в части книги, посвященной механике, нам понадобится больше логики, связанной с вводимыми данными. Ранняя настройка этой системы обеспечивает быструю итерацию и масштабируемость с новыми схемами ввода, такими как контроллеры. Создайте метод intAdd: private void OnTestInput(InputAction.CallbackContext actionContext) { // Если действие было выполнено (нажато) в этом кадре if (actionContext.performed) { Debug.Log(IntAdd(addA, addB)); } }
Волшебство здесь в том, что метод размещается на выполненном testInput, который мы включаем в данном скрипте. Скрипт вызывается с действием, назначенным вводимыми данными. Пока что мы используем только простую логику, позволяющую журналу отладки показать, если выполняется actionContext. В нашем случае при вызове метода все будет работать правильно. Если бы нам нужна была другая логика, например если время восстановления навыка еще не было завершено, мы могли бы сказать пользователю, что он не может использовать этот навык в рамках этого метода. Это очень мощная система, которая должна быть чрезвычайно надежной.
64 Программирование Вернувшись в Unity, щелкните скрипт в иерархии, а затем введите некоторые значения в переменные addA и addB в инспекторе. Запустите игру и нажмите клавишу b. Вы должны увидеть изменение totalAdd, а также консоль выведет число из строки Debug.
Заключение Возможно, вы впервые читаете что-либо о программировании. Просмотрев эти небольшие примеры, вы получите прочные основы. Не обязательно сразу полностью понимать то, что мы рассмотрели в этой главе, так как сейчас это – «клей», скрепляющий остальные главы. Мы будем использовать все эти функции позже при программировании, а также добавлять новые библиотеки и различные реализации классов. Сейчас вы изучили основы программирования; мы будем тщательно опираться на них на протяжении всей книги. Если вы вдруг попали в тупик или что-то работает неправильно, обратитесь к GitHub, где есть все сценарии в их завершенном виде. Это конец основ, на которые мы будем опираться в следующих пяти главах. Часть II «Сборка и дизайн» дополнит все, что вы уже узнали, ответит на дополнительные вопросы о создании дизайна нашего прототипа и покажет, как Unity может помочь вам создавать игры с максимально возможной легкостью. Давайте перейдем к созданию персонажа и соединим некоторые из этих навыков программирования, чтобы заставить Мивари (Myvari) двигаться согласно введенным нами данным.
4 Персонажи В главе 2 «Дизайн и прототип» мы обсуждали, что в этой книге для нашей модели будет использоваться метод вертикального среза. Будучи вертикальным срезом, проект представляет собой упрощение игры. Она похожа на демоверсию, но в ней есть все основные механики игры, только в более простой форме. Вертикальный срез предназначен, чтобы дать инвесторам и игрокам убедительный пример того, что в дальнейшем станет полноценным игровым опытом. Мы покажем поведение персонажа, чтобы привлечь внимание игрока, а затем перейдем к небольшой части истории с механикой, основанной на загадках окружающей среды, и узнаем о прошлом главного героя. Начнем с понятий, связанных с главным героем; затем смоделируем персонажа и внесем изменения по своему усмотрению, проработав механику и движение. Также сделаем для них риг для дальнейшего анимирования. После этого поместим их в Unity и протестируем. Четвертая глава будет наполнена большим количеством информации, и мы рассмотрим множество различных концепций создания нашего персонажа и его правильного движения: дизайн и концепт, риг, управление персонажем, скрипт движений персонажа. Давайте приступим к созданию Мивари, главной героини нашей небольшой истории.
Дизайн и концепт Чтобы создать персонажа, может потребоваться множество измерений. Естественно, необходимо сделать все, чтобы главный персонаж нашего вертикального среза Мивари была максимально детализирована. Один из лучших инструментов для этого – спрашивать «почему?» для каждого аспекта персонажа. Примерный список вопросов: какой она расы и почему это имеет значение для истории? почему женский пол? почему ее история скрыта?
66 Персонажи как выглядит ее одежда? как выглядит ее раса? она человекоподобная? Подобными вопросами нужно задаваться на протяжении всего создания. К тому же ответы должны вызвать больше вопросов. Сведите их к минимуму. Да, это может показаться утомительным, но зато, когда вы закончите, вы будете знать, как персонаж ведет себя при встрече с кем-либо, его выражения лица, поведение, внутренние шутки, семейную предысторию и многое другое.
Время концепции! Теперь, когда у нас есть четкое представление о Мивари и о том, кто она такая, мы можем нарисовать концепт-арт. Начнем с некоторой работы с пропорциями и с набросков, чтобы найти ее базовый внешний вид. На рис. 4.1 слева мы также изобразили поведенческий подход. Это дает визуальное представление о том, как она может вести себя в анимации бездействия. Анимация бездействия – это анимация, которая возникает, когда ваш персонаж какое-то время остается неподвижным. Мы хотим, чтобы ее характер был прилежным и любознательным, поэтому она достанет книгу и просто начнет учиться.
Рис. 4.1. Первые наброски Мивари
После того как мы нарисовали эскизы того, как она может выглядеть, в том числе дали представление о ее личности, нам нужно подобрать цвета для завершения дизайна. Цветовая схема, показанная на рис. 4.2, была выбрана после ответов на все предыдущие вопросы.
Время концепции! 67
Рис. 4.2. Цветовая схема Мивари
Общие цветовые темы Мивари создают ощущение королевской власти, любопытства и безопасности. Они изображены в царственной одежде с золотыми линиями, обозначающими королевскую власть. Синий вызывает чувство безопасности – психологический и физический эффект, поскольку этот цвет снижает кровяное давление. Использование синего цвета немного больше вовлекает игрока в ее персонаж, ему любопытно увидеть этот ярко-синий среди остальной части ее одежды нейтрального цвета. Ее самым уникальным аксессуаром является ожерелье, имеющее механическое назначение. Это будет ключом к головоломкам, с которыми ей придется взаимодействовать. По этой причине цвет ожерелья выделяется на фоне остальных изделий Мивари. Этот цвет также будет уникальным в окружающей среде при использовании концепции, известной как руководство пользователя, о которой мы поговорим в главах 5–7 и главе 12 «Последние штрихи». Нам нужно будет постоянно использовать этот цвет в окружении, механике, а также при полировке всего вертикального среза. Как только у нас появится четкое представление о личности и цветах персонажа, нужно перейти к 3D-версиям концепции. На данном этапе лучше всего определяется характер. На следующем изображении показаны черты лица персонажа с помощью инструмента sculpting. Наша команда имеет большой опыт использования ZBrush от Pixologic, что позволило создать скульпт, по-
68 Персонажи казанный на рис. 4.3. Такие софты, как 3DCoat и Blender, также предлагают инструменты для скульптинга.
Рис. 4.3. Скульпт головы Мивари с высоким разрешением
После того как мы проработаем достаточное количество итераций со скульп тами, будем использовать это как начальную модель с высоким разрешением. Теперь, когда мы определили наш основной скульпт с высоким разрешением (high-res), перейдем к получению оптимизированной модели с низким разрешением (low-res) из ZBrush. Именно модель с низким разрешением, как на рис. 4.4, будет находится в игре. Когда вы создаете здания, флору, фауну или персонажей, эта модель будет служить для них универсальным масштабом. Есть несколько способов работать с масштабом, однако я знаю, что этот персонаж является главным существом в этой игре; все предметы будут окружающей средой или реквизитом, и все они будут уменьшены до ее размера в качестве базового масштаба.
Рис. 4.4. Скульпт головы Мивари с низким разрешением
Риггинг 69 Некоторые другие способы масштабирования могут быть выполнены путем построения в масштабе. Единицы измерения – сантиметры. Если вы строите до метра, а затем экспортируете из своего создания цифрового контента (DCC) как сантиметр, все будет соответствовать единой шкале единиц. Это отличный способ сборки, если вы создаете большую игру, в которой работает множество команд по всему миру. Вы также можете построить игру вокруг самой окружающей среды. Примером этого может быть игра с видом сверху вниз с использованием квадратов для создания среды, поэтому все это работает вместе. Квадраты могут занимать десятую часть экрана. На основе этой информации вы сделаете снимок экрана с ожидаемым разрешением, а затем нарисуете концепции персонажей на этом изображении, чтобы они соответствовали масштабу. Посмотрите на свой проект и обратите внимание на единую точку масштаба, на которой вы будете строить игру. В конечном итоге это сэкономит вам время. Потенциально вы можете смоделировать объект, а отмасштабировать в игре после его импорта. Однако есть риск, что механика может работать неправильно. Вспомните такие игры, как Tomb Raider или Assassin’s Creed, где главный герой должен карабкаться по стенам. Для правильной работы данной механики необходимо, чтобы у персонажа был конкретный рост и большое количество условий, чтобы он находился в нужном месте в нужное время во время анимации.
Риггинг После проработки этапа создания концепции нам нужно поработать над костной системой персонажа для ее дальнейшей анимации. Мы будем использовать Autodesk Maya 2022. Пункты, которые мы рассмотрим, будут принципами, а не техническими деталями. В зависимости от вашего инструмента DCC вы можете столкнуться с несколько иной терминологией, однако следующие термины обычно применяются к любому из основных DCC, используемых для разработки игр.
Мышление под анимирование При риггинге наиболее эффективным способом работы является подробный разговор о самих анимациях с художниками, которые будут за них отвечать. Даже если вы делаете анимацию самостоятельно, успешные риги гарантируют, что аниматору не нужно объяснять, что делает каждый элемент управления. Могут быть какие-то технические детали, но в целом, если элементу управления не нужно что-либо, его следует заблокировать и скрыть. Когда специалист по анимации перемещает элемент управления, который отвечает за движения рукой, он, как правило, ожидает, что там же будут находиться все элементы управления пальцами. Это интуитивно понятно, но не следует думать, что аниматору это действительно нужно. Возможно, он захочет, чтобы все элементы управления отдельно находились под их контролем.
70 Персонажи
Деформация Это способность сетки изгибаться заданным образом, например в локтях или коленях. Когда вы знаете, как они будут изгибаться, то можете спланировать свою сетку так, чтобы эта деформация была возможной с правильным edge flow в вашей модели. Edge flow сам по себе является искусством, обеспечивающим достаточную геометрию для сохранения формы при деформации. Пример представлен на рис. 4.5. Не торопитесь сразу работать с другими примерами моделей, сначала изучите, как каждая структура тела может изгибаться.
Рис. 4.5. Пример edge flow на гуманоиде
Лицевая деформация на сегодняшний день является наиболее специфической. Если вы планируете какую-либо деформацию лица, когда начнете риггинг, поищите несколько видеороликов, объясняющих, как для этого настроить персонажа. Выражения лица сложны, и сделать их сразу же правильно очень непросто. Небольшой пример edge flow и правильного моделирования сетки на рис. 4.6. С правильно выполненной деформацией лица у вашего персонажа будут соответствующие выражения. Это может добавить изюминку в игровой опыт, усиливая погружение через эмоции. Если вы планируете показывать своего персонажа крупным планом, необходимо уделить время на работу с деформацией лица.
Иерархия Иерархия и родительские связи – неотъемлемая часть знаний по риггингу. Когда импортируют скелетную сетку в Unity, могут образоваться изменения, которых не было в вашем DCC. Иногда это может быть групповое или иерар-
Риггинг 71 хическое изменение, имеющее собственное преобразование. Unity увидит эти элементы как GameObjects и поместит их в одно и то же место в иерархии скелета. Каждое приложение может немного отличаться. Примером этого является Maya: если в вашей иерархии есть групповая нода (узел) в качестве родителя рига, Unity будет думать, что это собственное преобразование, и импортирует его как таковое. Если отсутствует логика в GameObject, вряд ли возникнут проблемы, но всегда стоит следить за этим. Если вы используете Maya, мы рекомендуем, чтобы у скелета, который вы будете привязывать, не было родительских нод.
Рис. 4.6. Edge flow лица
Сейчас мы подойдем к интересному в работе: control rig. Самым кастомизируемым ригом, с которым мы когда-либо работали, был риг привязки (binding rig), привязанный ко всем вершинам персонажа. Затем это приводилось в движение дублирующим ригом (duplicate rig), который мы назвали control rig. Это позволяло ригу привязки беспокоиться только о входных данных одного объекта. Это важно, когда вам понадобится несколько деформирующих инструментов, перемещающихся по ригу. Еще, как вариант, вам понадобится отдельное управление сквошем и вращением. Вся эта логика управляющего рига будет управлять ригом привязки, и вы не будете беспокоиться о разрыве привязки к персонажу.
Кости или суставы Кости и суставы – термины, взаимозаменяемые в риггинге. Связанный скелет состоит только из костей или суставов в своей собственной иерархии от основания через позвоночник, руки, ноги, шею и голову. Эти кости можно увидеть на рис. 4.7 ниже.
72 Персонажи
Рис. 4.7. Примеры суставов персонажа в DCC
После того как вы «разложили все по косточкам», нужно спланировать следующий уровень системы ограничений, которые будут управлять структурой костей. Если вы используете control rig сверху, ограничения вместо этого будут управлять костями control rig.
Прямая кинематика / инверсная кинематика Прямая кинематика (FK – Forward Kinematics) и инверсная кинематика (IK – Inverse Kinematic) – две основные формы анимации рук и ног. FK – это техника, при которой аниматор вручную вращает каждый сустав по отдельности. Если вы анимируете руку, то должны начать с плеча, затем локоть, затем запястье и т. д. Это называется «прямая», так как вы идете прямо по иерархии для анимации. Напротив, в IK вы анимируете руку, а плечо и локоть будут следовать за направлением плоскости вращения. Какую из них использовать – вопрос спорный, однако все же они являются инструментами. Если вам легче работать с конкретным инструментом, используйте его. Очень часто можно увидеть переключатели FK / IK на ригах персонажей, поскольку оба они имеют свое место в определенных рабочих процессах анимации. Кроме того, основная задача IK заключается в том, чтобы удерживать руку в том же положении или в том месте, где она была помещена в последний раз. Представьте, что вы встаете и поднимаете руку вверх, затем двигаете бедрами вверх и вниз, удерживая руку в воздухе на одном и том же месте. Было бы очень утомительно анимировать с настройкой только FK, так как вам пришлось бы отдельно настраивать плечо, локоть и запястье для всех движений бедра. С помощью IK вы сможете поместить запястье в то место, где оно должно быть, а затем просто анимировать бедра. IK позаботится о плече и локте за вас. Тем не менее для цикла ходьбы лучше подходит FK,
Риггинг 73 в которой сила тяжести действует на руку и она в основном просто описывает дугу с импульсом движения. Данные инструменты потенциально могут дать одинаковый результат. По мере накопления опыта работы с инструментами вы получите представление о стиле анимации.
Ограничения Ограничение – простое действие. Чтобы аниматоры сразу понимали назначение элемента управления, используются визуальные объекты. В качестве крат кого примера можно привести кривую NURBS (неоднородный рациональный базисный сплайн – точка в пространстве, которая создает что-то визуальное), которая будет указывать на каждый палец в манипулируемой руке, помогая сжать кулак. На рис. 4.8 ниже показано, как мы сделали это с ригом Мивари.
Рис. 4.8. Элементы управление руки Мивари
Они называются элементами управления просто потому, что позволяют аниматорам управлять определенными аспектами персонажа. Напоминает другой термин, который мы ввели в главе 1: наследование (parenting). Действительно, между ограничением и наследованием есть сходство, однако в случае ограничений нам разрешено быть конкретными в том, что именно мы хотим ограничить. В Maya это разделено на ограничения перемещений, вращений и масштабирований. Вы также можете ограничить отдельные компоненты каждого из них, например только «повернуть по оси X». Так риггер немного ограничивает аниматора. В приведенном выше примере с управлением головой может потребоваться только ограничение диапазона вращения. Таким образом, вращение головы не повлияет на кости. В случае с наследованием вы не сможете их разделить, так как родители влияют на все преобразования дочернего объекта.
74 Персонажи
Деформеры Инструменты деформации будут уникальными для каждого DCC. Основная функция деформера состоит в том, чтобы определенным образом управлять иерархией верхнего уровня. Примером этого может быть деформер скручивания (twist deformer), который позволяет легко создавать анимацию скручивания. В некоторых продвинутых анимациях используются деформеры посредством другого меша, управляющего костями, который называют ribbon rig, как показано ниже на рис. 4.9. Слева на рисунке показана «лента» и деформеры, которые контролируют основы ожерелья. Справа – то, что видит аниматор, скрывая базовый элемент управления.
Рис. 4.9. Пример ribbon rig для ожерелья Мивари
Controls Работа аниматоров в трехмерном мире уникальна – представления предметов, которые им нужно перемещать в реальном времени. Будь то рамка элементов управления, окружающая руку или голову, каждая форма дает аниматору контроль. Каждый персонаж будет иметь элементы управления, уникальные для их нужд. Мивари будет иметь стандартные элементы управления для человекоподобного персонажа и дополнительные – для ее одежды и безделушек. На рис. 4.10 отображены элементы управления всем телом нашего персонажа.
Анимация на основе физики Некоторую анимацию можно сделать с помощью симуляции. Для этого к мешу должна быть прикреплена кость, а DCC будет выполнять физические движения, которые будут немного ограничены. С ними очень хорошо работать для цепей, ожерелий и вообще всего твердого или болтающегося. Эти вещи, как известно, сложно анимировать вручную, поэтому лучше всего позволить программе сделать это за вас. В некоторых случаях игровой движок может обрабатывать всю анимацию, основанную на физике, что облегчает жизнь аниматорам. То есть анимация на физике будет независимой от самого файла анимации, что позволит более плавно смешивать анимации.
Система инверсной кинематики человека (HIK) Компания Autodesk создала систему biped rigging, позволяющую легко интегрировать несколько программ. Это необходимо в первую очередь для работы с захватом движения, когда анимация создается с помощью нескольких отдельных технологий. Захват движения осуществляется с помощью костюмов, устройств для захвата лица, а также, например, специальных перчаток.
Риггинг 75
Рис. 4.10. Управление Мивари
Основная цель рига Инверсной кинематики человека (HIK – Human Inverse Kinematics) – собрать данные для анимации головы, позвоночника, рук и ног человекоподобного персонажа. Существует расширенная версия, которая позволяет использовать пальцы, а также дополнительные функции на руках и ногах, например скручивание. Чтобы собрать больше информации о скелетах HIK, Autodesk опубликовал документацию о том, как лучше всего их использовать. Для наших демонстраций мы не будем использовать систему HIK. С нашей Мивари мы будем делать всю анимацию вручную и не будем работать с мокапом. Зная это, мы решили придерживаться только собственного рига и системы управления.
Анимация Мы спроектировали, смоделировали и оснастили персонажа элементами управления. Теперь можно использовать навыки анимации в нашем DCC, что-
76 Персонажи бы оживить персонажа. Когда вы решаете, какую анимацию сделать, не забудьте хорошо подумать о характере персонажа. Поскольку мы выделили много времени на все трудные вопросы о мотивах и желаниях персонажа, мы просто обязаны передать все это ее правильными движениями, ожидаемыми от человека с выбранной личностью. Работа с анимацией – это искусство. Во-первых, нужно поработать с фазой блокинга для ключевых поз, чтобы правильно рассчитать время. В каждом ключевом кадре должна быть индивидуальность. Если вы смотрите на ключевой кадр и не можете получить представление о персонаже, то он не будет считаться «ключевым» по отношению к персонажу. После того как вы заблокировали некоторые ключевые кадры и подвигали их, чтобы получить представление о времени, вы должны добавить промежуточные кадры – кадры между ключевыми позами. Так создастся движение в каждой ключевой позе. Как только вы дойдете до этого момента, лучше добавить его в игровой движок, чтобы получить ощущение фактического движения с помощью контроллера персонажа, тем самым увидеть, преобразуется ли непосредственно в игре то, что вы видите в DCC. Делать это на данном этапе разумно, так как у вас будет больше времени ближе к концу проекта, чтобы отшлифовать все анимации после того, как научитесь двигать своего персонажа.
Контроллеры персонажа Теперь, когда мы собрали дизайн персонажа, модель и риг, нужно настроить контроллер, чтобы они реагировали на входные данные. Есть два основных метода для контроллеров персонажей. Встроенный контроллер персонажа, который предоставляет Unity, позволит вам ходить персонажем, подниматься по лестнице и легко встраивать дополнительные функции взаимодействия, но у него есть свои ограничения. Самым большим ограничением является то, что он не используется в качестве физического объекта. Если вам нужно, чтобы вашего персонажа двигала физика, есть второй вариант. Второй вариант – использование Rigidbody и капсулы столкновения (collision capsule) с символьным скриптом, использующим их в качестве ограничений для физического движка. Как вы, возможно, уже догадались, чтобы выбрать правильный вариант, необходимо задавать вопросы! Некоторые из них: какова основная механика? нужна ли мне физика движений? сколько будет других ограничений для моего персонажа? Через какое-то время вы научитесь задавать эти вопросы на ранних этапах разработки, начнете понимать, какая архитектура может понадобиться для реализации желаемого игрового процесса в Unity. Тут вам поможет метод проб и ошибок на практике. Не расстраивайтесь из-за ошибок, ведь это лучший способ обучения. Правильные действия не всегда очевидны. Задайтесь вопросом о том, какова основная механика. Глядя на свою игру, вы можете попробовать создать оружие в игре, но во время производства боевая механика более увлекательна. Поняв это, можно попробовать сократить большую часть крафта и приложить больше усилий для полировки механики
Сценарий движения вашего персонажа 77 боя. То есть сделать больший акцент на контроллере персонажа, а не на пользовательском интерфейсе или косметической работе. Мы решили использовать простой подход к движению персонажа. Нам нужно только передвижение, а все остальные взаимодействия будут в основном через положение мыши и работу камеры. Имея это в виду, мы будем основываться на контроллере персонажа.
Встроенный контроллер персонажа В Unity есть компонент встроенного контроллера персонажа, что даст прочную основу для работы. Это капсульный коллайдер (Capsule collider), который позволяет легко перемещаться в игре от первого или третьего лица. Интересная часть этого заключается в том, что он не использует физику или Rigidbodies для физики. В документации Unity это лучше всего объясняется как контроллер в «стиле игры Doom»: очень быстрое движение и резкая остановка персонажа. Такая механика встречается не часто. Если вы делаете игру, в которой требуется чрезвычайно жесткое управление, тогда вы точно по адресу. В игре Metroid используется данная методика, чтобы резко поворачивать персонажа влево и вправо. Согласитесь, если бы приходилось замедляться до полной остановки перед каждым поворотом, игра не была бы такой классной. Приятно то, что коллайдер можно легко и быстро применить на персонаже для проверки чего-то простого. Однако, если вы хотите добавить прыжки, плавание, полеты или что-либо, связанное с физикой, этот коллайдер не будет работать без большого количества проделанной работы. В этом уроке мы будем использовать встроенный контроллер персонажа, поскольку Мивари нужно находиться только на земле, не прыгая и не скользя, и ни одно из ее взаимодействий не использует сфокусированной физики.
Контроллер персонажа Rigidbody Данный вариант начинается с написания кода и предполагает большую гибкость, которую встроенный контроллер не может обеспечить для многих целей. Если вы хотите использовать разные физические материалы в своей игре, то Rigidbody именно для вас. Также если в игре планируется использовать физику несколькими способами, то было бы лучше запланировать работу с Rigidbody и компонентами столкновения в качестве выбора физики на контроллере персонажа.
Сценарий движения вашего персонажа Когда вы пишете сценарий своего персонажа, следует провести как можно больше разговоров о дизайне движений, чтобы понимать, что нужно разработать. Для Мивари мы хотели внести некоторые особенности, связанные с движением в окружающей среде, потому что игра представляет собой игру-головоломку с окружающей средой. Мы должны заставить окружающую среду взаимодействовать с ней, когда она пересекает ее. Вот список того, что мы прошли: бездействие, ходьба, на земле, в воде,
78 Персонажи в горах, вращение. Есть два скрипта, связанных с движением, которые мы не решились реализовать, – бег и прыжки. Причина, по которой мы не собираемся реализовывать их в настоящее время, заключается в том, что мы не знаем наверняка, нужны ли они. Сейчас нам приятно просто ходить по игровому миру, и мы хотим, чтобы игрок также обращал большое внимание на окружающую среду. Если в будущем это все же потребуется, тогда мы настроим контроллер персонажа так, чтобы он принимал движение бега. С прыжками аналогично, но игра совсем не требует механики каких-либо прыжков. Любая механика реализуется только для удовлетворения потребности игры. Мы можем обнаружить потребность после некоторых тестирований, а игроки скажут, что им нужно прыгать. Если это действительно станет необходимым – пойдем на уступки.
Первоначальная настройка в Unity Для начала, чтобы настроить Мивари как минимум на прием скриптов для передвижения, нам следует совершить некоторые настройки в Unity. Мивари уже должна быть импортирована в проект Unity. Чтобы вы знали, сделать это можно простым перетаскиванием в папку проекта. Если вы выберете SM_Myvari в папке Character, инспектор покажет настройки импорта модели, как показано на рис. 4.11. Используемые здесь настройки по умолчанию нам подходят.
Рис. 4.11. Вкладка Model в настройках импорта
Далее нужно перейти на вкладку Rig и настроить риг. На рис. 4.12 у нас есть несколько вариантов. Убедитесь, что для параметра Animation Type установлено значение Humanoid. Мы также хотим создать аватар из этой модели, а затем настроить его. Так откроется другое окно для настройки костей гуманоидной структуры.
Сценарий движения вашего персонажа 79
Рис. 4.12. Вкладка Rig в настройках импорта
По умолчанию в этом окне будет отображаться тело, хотя на рис. 4.13 мы показываем только головную часть. Лучше всего пройти через каждую часть тела, так как система аватара сделает все возможное, чтобы выровнять суставы в нужное место, однако срабатывает это не всегда хорошо. Если он не установлен должным образом, просто сделайте правильное соединение по точкам и слотам.
Рис. 4.13. Настройки импорта Rig, конфигурация головы
Прежде чем настраивать контроллер, обсудим наши решения о том, как будет проходить игра. У нас есть стиль игры от третьего лица. Соответственно, мы должны добавить камеру к персонажу. Таким образом, нужно сделать префаб с нашим персонажем и камерой. Для этого создаем иерархию, которая позволит независимое движение камеры, но сохранит ее привязку к персонажу. На рис. 4.14 показано, как мы настроили префаб.
80 Персонажи
Рис. 4.14. Иерархия префабов персонажа
Причина, по которой мы настроили его таким образом, заключалась в том, что мы хотели, чтобы контейнер содержал как камеру, так и персонажа. GameObject персонажа содержит все скрипты, необходимые для персонажа. Меш будет содержать аниматора и аватар. Риг камеры будет содержать камеру, а также скрипты, необходимые для поддержания камеры в желаемом положении. Позже в главе 6 «Взаимодействия и механика» мы довольно подробно рассмотрим Cinemachine, поскольку в некоторых частях игры нам нужно будет поместить камеру для синематиков. В оставшейся части этой главы поясняются только основы настройки движения персонажа. Чтобы заставить персонажа GameObject двигаться, давайте настроим компоненты. На рис. 4.15 мы добавили еще четыре компонента – character controller, наш скрипт движений Мивари, компонент Rigidbody и систему player input.
Рис. 4.15. Компоненты GameObject
Как уже известно, мы будем использовать базовый контроллер персонажа. Эти настройки субъективны, и мы их не дорабатывали, но это то, что у нас есть на данный момент. Одно замечание, которое мы должны добавить, касается center attribute. Именно здесь, по мнению контроллера персонажа, находится центр персонажа. По умолчанию Мивари находится на земле, но вам нужно переместить центр немного вверх, чтобы капсула была ближе к центру и немного от земли. Мы поместим центр рядом с тазом, а затем используем радиус и высоту, чтобы охватить все тело персонажа. Делаем это потому, что таз контролирует общую высоту, поскольку центр масс человеческого строения находится в пупке.
Сценарий движения вашего персонажа 81 Пока что пропустим сценарий движения. Rigidbody здесь должен помочь с будущими потребностями механики и работой, основанной на физике. Мы рассмотрим это в главе 5 «Окружающая среда» и в главе 6 «Взаимодействия и механика». PlayerInput – это система в Unity, которая настраивает модульные вводы для упрощенного добавления различных систем ввода без необходимости изменения кода. Во-первых, откройте Package Manager и посмотрите, установлен ли Input System. Он должен быть частью Unity Registry. Если он не установлен, обязательно установите! Если установлен, то нужно создать систему ввода, с которой мы могли бы работать. Для этого добавьте новый ассет под названием Input Actions, как на рис. 4.16.
Рис. 4.16. Добавление ассета Input Actions
82 Персонажи После создания входного воздействия назовите его в соответствии с вашими потребностями. Мы назвали Player Actions. Эта группа входных воздействий используется непосредственно для любых необходимых действий игрока. В будущих проектах вам могут понадобиться и другие действия, помимо персонажей. Сейчас дважды щелкните ассет, чтобы открыть окно Input Actions. Здесь мы разработаем параметры входных данных, которые в настоящее время необходимы для Мивари. На рис. 4.17 показана завершенная система ввода в том виде, в каком она нам нужна в данный момент. Мы можем добавить больше входных данных, поскольку вертикальный срез продолжает развиваться.
Рис. 4.17. Input Actions
Action Maps – это группы, у которых есть собственный набор действий, к которым можно обращаться. Properties – это детали и параметры выбранного действия. В этом случае нам нужны только входные данные для Мивари, поэтому мы создали сопоставление действий Мивари. Обратите внимание на использование заглавных букв в имени Action Mapping, так как оно будет использоваться далее, как только мы перейдем к скрипту движения. Разделы с зеленым цветом в Actions – это сами действия, синие – привязки, а светло-красные – части привязок. Для Locomotion нас интересует только набор векторов. При добавлении новой привязки, если вы нажмете символ плюс (+) справа от действия, у вас есть два варианта. Это Binding или 2D Vector Composite. Когда вы нажимаете 2D Vector Composite, он автоматически добавляет составные части Up, Down, Left и Right. В настоящее время мы определяем их как ввод с клавиатуры, чтобы придерживаться определенной системы ввода. Есть очень интересный и полезный инструмент при настройке действия – кнопка Listen. Глядя на рис. 4.18, вы можете видеть, что она нажата и ожидает ввода. Возможность нажать предполагаемую кнопку дает нам ощущение немедленной обратной связи с игроком. Если сейчас кажется странным назначать нажатие клавиши действию, то во время игры это не изменится.
Сценарий движения вашего персонажа 83
Рис. 4.18. Прослушивание ввода
Ввод Look предназначен для движения камеры, а Delta – для движения мыши. Действие Aim предназначено для тех случаев, когда вы удерживаете правую кнопку мыши для эффекта зума. Это выбор Action Type в качестве кнопки и ожидание ввода правой кнопкой мыши. Наконец, у нас есть кнопка Interact (взаимодействие). Это то же самое, что и Aim, но предназначено для нажатия клавиши E в определенное время. Это время будет определено в главах «Окружающая среда» и «Взаимодействия и механика». Теперь у нас есть настройки для входных данных, которые мы, как игроки, вкладываем в игру. Даже если бы мы написали скрипты для работы с этой системой ввода, это ни на что не повлияло бы. Итак, прежде чем начать писать скрипт, нам нужно собрать основы настройки анимации для Мивари. Давайте посмотрим на анимации, которые нам понадобятся. На данный момент нужны только Idle и Walk для перехода анимации. Сейчас пока что не нужно настраивать Interact, поскольку в настоящее время мы не используем его. Использование Interact подробнее рассмотрим в главе 5.
Бездействие Скорее всего, Мивари придется какое-то время стоять на месте, пока игрок осматривается. В большинстве случаев при бездействии скрипты не используются, так как это должно быть стандартным состоянием вашего контроллера анимации. Когда вы вводите персонажа в свою сцену, вам нужно добавить компонент Animator; см. рис. 4.19 для правильной конфигурации.
Рис. 4.19. Компонент Animator
Controller и Avatar будут пустыми. Чтобы создать контроллер, создайте новый ассет и перейдите в Create > Animator Controller. Контроллер – это интерфейс между кодом и визуальными элементами для перемещения скелетной сетки, которую мы хотим анимировать. С Idle мы создадим состояние по умолчанию и назовем его Idle. Вы можете увидеть это на рис. 4.20. В папке Characters > Animations внутри проекта
84 Персонажи есть анимации, которые мы настроили для Мивари. Выберите состояние Idle и перетащите анимацию бездействия из этой папки в параметр Motion в инспекторе, как показано на рис. 4.21 ниже.
Рис. 4.20. Конечный автомат Controller
Рис. 4.21. Инспектор состояния анимации бездействия
При наличии анимации бездействия, когда вы нажимаете Play, персонаж переходит в режим бездействия и зацикливает эту анимацию бесконечно! Мы также хотим иметь анимацию ходьбы. Чтобы добавить ее, в пустом месте щелкните правой кнопкой мыши и выберите Create State, а затем – Empty. Назовите его Walk. Выберите его и добавьте анимацию ходьбы. После этого щелкните правой кнопкой мыши по Idle и выберите Make Transition, затем щелкните левой кнопкой мыши состояние Walk. Таким образом совершится переход из режима бездействия в режим ходьбы. Сделайте то же самое из состояния Walk обратно в Idle. Вот так настраиваются параметры для перехода между режимами. Теперь мы добавим в контроллер параметр isWalking, как показано на рис. 4.22.
Сценарий движения вашего персонажа 85
Рис. 4.22. Параметры контроллера
Раздел Parameters находится в верхней левой части контроллера. Создайте логическое значение и назовите его isWalking. Мы будем использовать этот параметр в точках перехода. Если вы выберете переход из состояния Idle в состояние Walk, вы увидите переход от одной анимации к другой в инспекторе. В нижней части инспектора находятся условия. Давайте добавим условие и установим для него isWalking is True. Состояние анимации Idle изменится на Walk, когда isWalking is true. Затем вы можете сделать обратное, чтобы вернуться в режим бездействия. Теперь у нас есть вводимые данные и анимация с переходом, готовым слушать логику персонажа. Сейчас нам нужно попасть туда и заставить скрипт движения работать. Давайте займемся кодом!
Точка ввода кода Мы хотели добавить сюда небольшой раздел, объясняющий, как мы будем работать с кодом. В главе 3 «Программирование» мы шаг за шагом прошлись по каждой строке, чтобы изучить основы кода. Здесь планируем сделать скрипт доступным для вас целиком, со всеми комментариями, чтобы вы могли сразу прочитать его. В оставшейся части этой главы мы изучим мелкие детали, которые не рассматривались ранее, и расскажем о них как об инструментах. Мы рекомендуем вам создавать свои собственные сценарии и работать с инструментами, которые мы используем для создания собственных сценариев движения персонажей. Мы будем работать с файлом MyvariThirdPersonMovement.cs. Здесь не все так просто, поэтому знайте, что не обязательно понимать полностью все, что обсуждается. Замечая сложные моменты и работая над ними, вы укрепляете свои знания и понимание того, как работать разработчиком в Unity.
RequireComponent Когда вы видите RequireComponent над определением класса, это говорит о том, что GameObject, к которому прикреплен этот скрипт, должен что-то иметь. В нашем случае мы хотим, чтобы MyvariThirdPersonMovement.cs был у персонажа, и нам нужно убедиться, что у него есть контроллер персонажа. Хорошо то, что, если Unity увидит, что GameObject, к которому вы прикрепляете контроллер персонажа, не имеет необходимого компонента, она просто прикрепит его к GameObject за вас! Разве это не круто? Это круто.
86 Персонажи [RequireComponent(typeof(CharacterController))]
Обновление кода Мы пройдемся по этой части немного подробнее, так как каждая строка подробно описывает предыдущую информацию, и трудно объяснить одну строку в конце, не показывая контекст. В первой части мы хотим убедиться, что, если персонаж находится на земле и его скорость не выше 0, она установлена на 0. Иногда игровые объекты будут двигаться небольшими шагами в направлении y. Это не обычное явление, однако иногда в 3D-софтах повороты и движения, вызывающие округление значений, и скорость могут увеличиваться, когда не должны. Этого можно избежать, используя следующие несколько строк кода. if (controller.isGrounded && playerVelocity.y < 0) { playerVelocity.y = 0f; }
Данный код мы разберем в следующем разделе, чтобы обеспечить подробное объяснение, ведущее к назначению сценария движения персонажа в редакторе. Если вы помните, в начале главы в разделе «Сценарий движения вашего персонажа» мы хотели настроить ходьбу по воде. Чтобы знать, как настроить эту логику, нам нужно проверить, находится ли персонаж в воде. Мы будем использовать метод Raycast из библиотеки Physics, который принимает аргументы, как показано во всплывающей подсказке на рис. 4.23 ниже.
Рис. 4.23. Аргументы Physics.Raycast
Когда используется Raycast, его аргументы следующие: origin, direction, hitInfo, maxDistance и layerMask. Мы определяем origin как позицию этого игрового объекта плюс одну единицу в направлении вверх. direction – направление вниз. hitInfo сохраняется как RayCastHit, именованное hit. maxDistance задан на две единицы. LayerMask задан в waterLayer. Для проверки создайте куб и выберите Water в качестве значения его слоя в инспекторе. Мы вызовем эту проверку waterLayer во время части controller. Move:
Сценарий движения вашего персонажа 87 // Проверка наличия воды standingInWater = Physics.Raycast(transform.position + Vector3.up, Vector3.down, out RaycastHit hit, 2f, waterLayer);
В следующей части Input System считывает значение движения, которое мы собрали вместе. Переменная движения – это Vector2 или только x и y. Итак, нам нужно убедиться, что это имеет смысл для движения в 3D. // считываем значения input action для данного скрипта Vector2 movement = movementControl.action.ReadValue();
Мы создаем Vector3 и помещаем x и y из прочитанного значения, сохраняя y Vector3 равным 0. // Используем значения из inputs и помещаем их в vector3, оставляя направление вверх нулевым Vector3 move = new Vector3(movement.x, 0, movement.y);
Теперь нам нужно подумать о персонаже и координации камеры. У нас есть переменная move, в которой указано направление movement, но камера может смотреть в другом направлении, отличном от прямого относительно вашего персонажа. Итак, давайте учтем это. // учитываем направление камеры вперед, так как оно должно быть относительно вида камеры move = cameraMainTransform.forward * move.z + cameraMainTransform.right * move.x;
Затем мы снова просто обнуляем это значение y. Если бы мы реализовали механику прыжка позже, нам нужно было бы изменить это значение с 0 на разные значения в зависимости от нужного прыжка. // на всякий случай обнуляем значение у ;) move.y = 0.0f;
Вот так. Теперь пришло время подвигать персонажа. Мы уже подумали обо всех возможных проблемах с камерой, персонажем и типом местности. Стандартный контроллер персонажей Unity имеет метод Move. Этот метод принимает один аргумент – Vector3. Он говорит персонажу, куда идти. Нам нужно задать несколько вопросов. Как быстро они движутся? Они в воде? Самое время изучить новенькое – тернарный оператор (ternary). Давайте дадим небольшое пояснение, прежде чем мы перейдем к следующей строке кода. Это тернарная функция. Здесь говорится следующее: Если для standInWater установлено значение true, оно равно значению waterSlowFactor. В противном случае это будет 1f. (standingInWater ? waterSlowFactor : 1f)
Это удобно! Мы можем легко замедлить персонажа на настраиваемое значение, и, если мы не в воде, он будет двигаться с обычной скоростью, которую мы уже разработали. controller.Move(move * Time.deltaTime * playerSpeed * (standingInWater ? waterSlowFactor : 1f));
88 Персонажи У нас есть гравитация, определенная в верхней части этого класса, и мы устанавливаем скорость на это значение гравитации, умноженное на изменение во времени, чтобы учесть частоту кадров. Это не будет учитываться, если только Мивари не будет стоять на земле из-за оператора if поверх функции обновления, который устанавливает velocity.y в 0, если она меньше 0 и isGrounded. playerVelocity.y += gravityValue * Time.deltaTime; controller.Move(playerVelocity * Time.deltaTime);
Здесь мы вызываем два метода для обработки состояний вращения и анимации. HandleRotation(movement); HandleAnimation(movement);
Методы Чтобы сделать цикл обновления как можно более чистым, мы переработали обработку вращения и анимации из функции Update. Рефакторинг – это процесс реструктуризации существующего кода, чтобы сделать его более читабельным. Он работает в обновлении, но вызывается только по одной строке для каждого метода. Первый метод, который мы хотели бы рассмотреть, – это метод HandleAnimation. В качестве входных данных используется Vector2, который является прямым вводом для чтения Vector2 из системы ввода. Нас беспокоит только один параметр анимации – isWalking. Сначала мы получаем значение bool в его текущем состоянии и сохраняем его в локальной переменной. Затем проверяем, является ли какой-либо из векторов во входном движении отличным от нуля, а также является ли isWalking в настоящее время false. Если это так, мы устанавливаем для аниматора bool значение true. В противном случае – значение false. Когда это логическое значение изменится, оно будет обновлено в контроллере и установит анимацию в соответствующее состояние. void HandleAnimation(Vector2 movement) { bool isWalking = animator.GetBool("isWalking"); if (movement != Vector2.zero && !isWalking) { animator.SetBool("isWalking", true); } else if (!(movement != Vector2.zero) && isWalking) { animator.SetBool("isWalking", false); } }
Это самый продвинутый метод, который у нас есть. Мы считаем разумной идеей как можно чаще выходить из зоны комфорта, чтобы продолжать развитие. Мы изучим данный метод, и, даже если это не имеет смысла прямо сейчас,
Заключение 89 пусть ваш мозг проработает это. Здесь происходят три действия. Нам нужно найти угол, на который мы хотим повернуться, получить значение поворота, а затем повернуть! Во-первых, targetAngle выполняет метод Mathf под названием Atan2. Atan2 – это метод арктангенса, позволяющий вам найти угол, учитывая целевое положение, куда вы хотите повернуть. Это интересный метод, который очень полезен в играх для вращения персонажей в 3D-софтах. Проблема в том, что нам нужно снова учитывать камеру. Atan2 возвращает радианы, поэтому нам нужно умножить его на константу отношения радиан к градусам, а затем добавить угол y камеры. Это смещение от угла персонажа. Затем мы берем этот целевой угол и создаем из него кватернион под текущим углом камеры по оси Y. Это позволит нам получить нужный угол, не беспокоясь о gimbal locking (Примерный перевод – блокировка осей. – Прим. перев.). Gimbal locking – это когда две оси застревают во вращении из-за того, что одна ось смещена от центра на 90°. Кватернионы не подвержены такому феномену, поэтому в конце мы переходим к кватернионам от углов Эйлера. По определению углы Эйлера ориентированы относительно фиксированной системы координат. Вот как мы представляем угол, под которым находимся в игре, где начало идет от 0, 0, 0 при вращении при импорте. Если вы повернете персонажа на 90° по оси Y, он будет в координатах 0, 90, 0 в областях вращения преобразования этого GameObject. Эти значения являются углами Эйлера. Наконец, нам нужно перейти к этому значению поворота. Мы делаем это через Slerp (spherical lerp). При работе с вращениями лучше всего использовать метод Slerp. Аргументами являются наше текущее вращение, новое вращение, которое мы только что сделали, а затем сколько времени потребуется, чтобы повернуться к этой новой позиции. Мы сделали эту скорость вращения общедоступной, чтобы можно было изменять ее на лету с целью получить переменную, которая лучше всего подходит. Void HandleRotation(Vector2 movement) { if (movement != Vector2.zero) { float targetAngle = Mathf.Atan2(movement.x, movement.y) * Mathf.Rad2Deg + cameraMainTransform.eulerAngles.y; Quaternion rotation = Quaternion.Euler(0.0f, targetAngle, 0.0f); transform.rotation = Quaternion.Slerp(transform.rotation, rotation, rotFactorPerFrame * Time.deltaTime); } }
Теперь персонаж может двигаться и вращаться. Это отличный первый шаг к созданию исследовательской игры, основанной на повествовании. Давайте закончим кратким изложением того, что было рассмотрено во всей главе.
Заключение В этой главе мы рассмотрели большое количество информации о персонажах. Мы прошли через дизайн, моделирование и риггинг, контроллеры персонажей, RigidBodies, некоторую работу в Unity и скрипты контроллера движения.
90 Персонажи Дизайн всегда будет сводиться к вопросу «почему?». Вы должны были вынести из этой главы, что «почему?» вашего персонажа и его мотивации помогут создать уникальный характер, который может быть родственным вам. Моделирование и риггинг сильно зависят от типа моделирования, которое вам нужно будет выполнить. Мы рассмотрели некоторые ключевые методы, способные помочь вам в моделировании думать в первую очередь об анимации. Это относится и к риггингу. Анимация будет завершающим этапом, и чем проще правильно анимировать, тем легче вам и вашим игрокам получать лучший игровой опыт. Над анимацией, как правило, работают постоянно, пока игра не будет близка к выпуску. Отнеситесь серьезно к дизайну рига, так как внесение изменений после запуска анимации может потребовать корректировок. Мы поняли, что встроенный в Unity контроллер персонажей имеет для нас наибольшую пользу, поскольку Мивари не должна зависеть от физики, как тряпичная кукла. Затем мы вошли в Unity, импортировали Мивари и просмотрели компоненты, необходимые для ввода, а также анимацию для нее. Наконец, мы закончили работу над сценарием движения и вращением персонажа. В следующей главе рассмотрим тему окружающей среды, ландшафта и инструмент под названием ProBuilder.
Присоединяйтесь к Discord! На момент написания этой книги у нас на сервере Unity работало более 200 профессионалов Unity, и мы постоянно адаптируемся, чтобы добавлять новые каналы для облегчения обсуждения таких ключевых тем, как программирование на C#, игровая механика, игровой пользовательский интерфейс, анимация, 3D-игры, звук и эффекты, а также специальный канал для авторов издательства Packt, чтобы они могли общаться с читателями книги. Расскажите нам о своих успехах и об идее игры, которую вы хотите создать вместе с этой книгой. Возможно, на канале вы найдете людей, в команде с которыми создадите игру. https://packt.link/unity3dgamedev
5 Окружающая среда Когда игрок входит в игру, в которой повествование в основном зависит от окружения, вам нужно сделать все, чтобы игрок ответил на большинство вопросов, которые он может задать. Мы посвятим время трем основным структурам разработки окружения – проектированию окружения, блокировке и итерации. Это должно звучать знакомо по работе с персонажами, которую мы делали ранее в главе 4! К счастью, существуют различия, которые мы подробно рассмотрим в данной главе. К концу главы вы ответите на достаточное количество вопросов об окружающей среде и поймете, как мы разработали наше повествование, а также это пригодится для будущей самостоятельной работы. Давайте разобьем темы, чтобы вы могли получить представление о главе: дизайн – эскизы, мудборды и режиссура, блокировка – блокировка сетки, Unity Terrain, Unity Probuilder, проработка процесса итерации, дизайн окружающей среды. Окружение в игре так же важно, как и персонаж. Необходимо глубоко подумать о нашем выборе с точки зрения того, насколько окружающая среда соответствует теме и повествованию игрового процесса. Здесь мы садимся за стол с концепт-художниками и дизайнерами, чтобы задать сложные вопросы. Проработайте как можно больше деталей, чтобы разработать цель для частей окружающей среды. Концептуализация среды должна с чего-то начинаться. Мы начали с набросков. У нас с самого начала было представление о том, каким должно быть окружение, поэтому мы решили набросать несколько быстрых концепций. После набросков мы собрали несколько мудбордов для лучшего определения стиля. Как только мы довольны стилем и общей концепцией, работаем в следующие три этапа, чтобы задать тон ключевым моментам: эскизирование, мудборды, режиссура. Рассмотрим эти этапы подробно, начиная с эскизов.
92 Окружающая среда
Эскизирование У вас могут быть сильные идеи о том, как вы хотите, чтобы окружающая среда выглядела. Как и в других этапах создания концепции, нужно потратить много времени на вопрос «почему?». Этот вопрос поможет определить контекст вашей окружающей среды, чтобы задуманный разработчиком опыт соответствовал опыту, получаемому игроком. Чтобы выполнить эскиз, можно использовать несколько методов. Ручка и бумага отлично подходят для быстрого создания набросков. Кому-то может прийти в голову отличная идея нарисовать и на салфетках в ресторане! Если вы сидите за компьютером, можете использовать Photoshop, если у вас есть подписка, или попробовать альтернативные программы, такие как Krita или GIMP, бесплатно. Потратьте некоторое время, чтобы набросать архитектуру, формы и ощущения. Каждый эскиз даст вам более подробное представление о том, каким может быть конечный продукт. После каждого быстрого наброска проведите небольшую беседу с собой или с командой, чтобы определить, нужно ли вам задавать дополнительные вопросы «почему?» в отношении окружающей среды. Количество требуемых набросков будет варьироваться в зависимости от того, насколько вы уверены в передаче эмоций, которые вы хотите, чтобы игрок испытал. Если вы не можете полностью описать аргументы в пользу каждой части, продолжайте рисовать и спрашивать «почему?». Со временем детализации будет достаточно, чтобы двигаться дальше. Ниже, на рис. 5.1, представлена серия изображений, и я кратко объясню, что они привнесли в дизайн среды для нашего вертикального среза игры. Мы действительно хотели нарисовать общие мазки типа окружения, что позволяет разрабатывать, не беспокоясь о тонкостях. Мы не искали архитектуру руин или флору окружающей среды. Изначально мы были на 100 % уверены в том, как будем собирать руины в горах, но нужно было просмотреть наши наброски, чтобы понять, что в итоге окажется правильным. Мы решили, что нам нужен отдаленный горный район, который настолько зарос, что все время чем-то напоминал пещеристую местность. Это даст ощущение, что вы на самом деле не на Земле, а на планете, достаточно похожей на Землю, чтобы чувствовать себя комфортно в формах природы. На этапе вашего обучения мы хотели, чтобы последний раздел, в котором вы заново раскрываете прошлое Мивари, радикально изменил ощущение местности. Первоначально это показано на нижнем правом концептуальном эскизе рис. 5.1. С него мы знали, что нужно поставить вопрос об архитектуре и определить средние формы. Продолжая разработку последнего арта, мы знали, что нам понадобится резкий контраст с точки зрения тематических визуальных эффектов, когда мир переместится в царство Мивари. Посмотрев на изображения на предыдущем рис. 5.2, вы увидите, что основное различие заключается в дневном и ночном времени. Это большое изменение само по себе, но мы также хотели, чтобы земля и деревья исчезли, одновременно возвращая архитектуре ее былое величие и возвышая неподвижную воду, которая отражает звезды в небе, тем самым действительно открывая это место как некое новое измерение.
Мудборды 93
Рис. 5.1. Примеры эскизов
Рис. 5.2. Последняя область головоломки
Дойдя до момента, когда набросаете концепции своей среды, вы захотите взять то, что узнали из набросков, и создать доску настроения (мудборд). Здесь вы можете закрепить рекомендации для создания своих произведений в будущем.
Мудборды Мудборд – это коллаж из изображений, которые определяют стиль и настроение какой-либо области. В интернете есть огромное количество изображений,
94 Окружающая среда которые люди нарисовали, визуализировали или даже сфотографировали, поэтому довольно легко найти изображения, близкие к вашему стилю и тональности, чтобы вы могли собрать их вместе для вдохновения на тот опыт, который хотите передать игроку. Если у вас есть окончательные наброски, самое время проявить себя с помощью мудбордов. Выделите некоторое время на поиск референса с похожими особенностями и ощущениями, например в архитектуре, и сделайте коллаж из того, каким должно быть настроение вашей окружающей среды. Так определится палитра цветов, с которой вы будете работать на этапе моделирования окружающей среды. Для нашего проекта было несколько основных требований. Мы хотели, чтобы горные джунгли привнесли в игру ощущение древней волшебной фэнтезийной цивилизации. Даже если вы будете запрашивать в поиске «волшебное фэнтези, древние руины джунглей», есть большая вероятность, что вы не сможете найти именно то, что нужно для референса. Вместо этого вы разбиваете основные функции каждой области и составляете мудборд. Для наших двух примеров мы пройдемся по мудбордам пещеры и руин.
Рис. 5.3. Пример мудборда пещеры
На рис. 5.3 мы сосредоточились на основной функции пещер. Каково это – быть крошечным в огромной пещере? Каковы атмосферные тенденции тумана и освещения? Мы получаем ощущение инопланетной среды, даже если все еще находимся на Земле, возможно, в неизведанной области нашей планеты.
Рис. 5.4. Пример мудборда руин
На рис. 5.4 мы хотели показать функцию руин в волшебной, фантастической цивилизации. К каким формам стремилось бы магическое существо, ориентированное на природу? Как цвета сочетаются друг с другом?
Блокирование 95 Мудборды должны отвечать за настроение и тон. Когда вы закончите поиск настроения, то поймете, что для каждой из ваших ключевых областей есть коллаж, лучше всего соответствующий настроению, которое эта область должна передать. В нашем случае нужно было прощупать разницу между пещерами и руинами. Обратите внимание, что они резко отличаются по настроению, даже если не разглядывать все мельчайшие детали изображений. Делать мудборды после эскизирования имеет смысл, только если вы можете получить ответы на вопросы с помощью эскизов. Однако, если вы обнаружите, что эскизы в целом только вызывают больше вопросов и запутывают необходимый стиль, вместо того чтобы определять его, тогда в первую очередь должны быть мудборды. Поиск референсов, как правило, дает визуальное представление ваших задумок.
Режиссура После быстрого наброска идей и чувств для ответов на вопросы «почему?» вы затем создали мудборд, чтобы еще больше укрепить ощущение окружающей среды. Следующий шаг – взять ранее определенную форму и настроение и использовать их, чтобы протолкнуть ваше повествование и механику на этапы игры. Первая локация, в которую вы приводите своих игроков, должна довольно быстро ответить на множество вопросов. К счастью, вы выделили время и ответили на как можно больше вопросов до этого этапа. Теперь можете с уверенностью создавать свой повествовательный дизайн. Мы знали, что для нашего проекта нам нужно будет как можно больше объяснить прошлое Мивари по мере прохождения в каждой части игрового мира. Когда вы строите сцену, постарайтесь представить там себя. Не торопитесь идти дальше и еще раз убедитесь, что на ваши основные вопросы даны ответы. Теперь давайте попробуем новый эксперимент: представьте опыт, который вы создали, через призму нового игрока. Попробуйте почувствовать то, что может почувствовать абсолютный новичок в 3D-играх. Достаточно ли подсказок, чтобы описать опыт для новичка? Затем посмотрите на это с точки зрения опытного игрока; отнимет ли добавление упомянутых подсказок интерес опытных пользователей? Данный процесс может потребовать несколько итераций. Будьте терпеливы и обязательно пройдите через это, чтобы быть уверенным, что вы можете объяснить предполагаемые движения персонажа. Отличный момент, чтобы показать кому-то свои наработки и узнать, какие непроизвольные вопросы возникнут; возможно, вы удивитесь. Свежий взгляд на каждом этапе покажет места, где требуется немного больше проработки. Через некоторое время картина того, что вам нужно на каждом этапе, станет более ясной, и тогда вы сможете перейти к этапу блокирования.
Блокирование Теперь, когда вы проработали столько концепций, сколько необходимо, у вас должно быть четкое представление о том, что сделает ваше окружение подходящим для повествования и персонажей. На этом этапе следующим шагом будет максимальное «блокирование»; смысл блокировки состоит в том, чтобы
96 Окружающая среда соединить все проработанные части в Unity с целью реализовать опыт, над созданием которого мы так усердно работали на предыдущих этапах. Сейчас вы осознаете и чувствуете себя комфортно с тем, что создали, можно поговорить о настроении и тоне в каждой части и настроиться на формирование общих концепций. Чтобы заблокировать уровень, мы будем использовать несколько инструментов, имеющихся в нашем распоряжении; Unity Terrain, Basic Shapes и Unity Probuilder настроят нас на создание основных элементов окружения.
Unity Terrain Работа с инструментом Terrain в Unity расширяет возможности. С помощью него очень легко начать и быстро создать красивый пейзаж. Для начала давайте создадим объект ландшафта. После этого мы рассмотрим настройки, рисование и листву, которые Unity предоставляет нам в качестве инструментов для создания ландшафта.
Создание ландшафта Первый шаг к работе с ландшафтом – его создание. Для этого есть два основных метода. Один из способов – щелкнуть по меню GameObject, затем 3D Object, а затем Terrain, как показано на рис. 5.5.
Рис. 5.5. Создание объекта ландшафта
Блокирование 97 Другой способ – щелкнуть правой кнопкой мыши по пустой области в иерархии, выбрать 3D Object, а затем Terrain. Любая из этих опций создаст в сцене игровой объект ландшафта с глобальными координатами 0, 0, 0 в плоскостях X и Z на 1000 единиц, каждая по умолчанию. Это значение может быть не тем, что вам нужно, поэтому давайте пройдемся по настройкам ландшафта.
Настройки В нашем вертикальном срезе мы будем использовать единицы измерения по умолчанию, так как это именно то, что нам нужно для ощущения масштаба. Для вашей игры может быть иначе. Одна приятная особенность ландшафта заключается в том, что его можно легко соединить с соседними тайлами ландшафта.
Рис. 5.6. Создание соседнего ландшафта
Первая опция в компоненте Terrain – это кнопка Create Neighboring Terrain. При выборе этого параметра вы увидите контур соседних тайлов, как показано на рис. 5.6. Если щелкнете по любому из этих квадратов, инструмент создаст новый ассет ландшафта, связанный с основным ассетом ландшафта. Теперь, когда вы понимаете, как инструмент Terrain может довольно легко соединяться с другими частями ландшафта, можете приступать к настройкам вашего ландшафта с точки зрения размера каждого тайла. Может случиться так, что ваш ландшафт должен иметь только 500 единиц в длину и 200 в ширину. Их стандартный знаменатель составляет 100 единиц, поэтому вы можете установить свои настройки, как показано на рис. 5.7 ниже.
Рис. 5.7. Настройки разрешения сетки
98 Окружающая среда Если вы планируете использовать траву или мелкие детали на своем ландшафте, убедитесь, что тайлы квадратные. Если ширина отличается от длины, инструменту Details Brush будет крайне трудно упорядочить картины. Нажатие на открытые квадраты заполнит их новым тайлом ландшафта. Щелчок по нескольким может привести к тому, что показано на рис. 5.8.
Рис. 5.8. Добавление соседних тайлов
Это дает вам свободу добавлять небольшие ответвления, если нужно немного больше места там, где вы не ожидали сначала. В нашем случае мы знали, что нам нужен только один блок размером 1000×1000, поэтому мы остановились на размере по умолчанию. Для простоты настройки весь наш вертикальный срез будет иметь место в одной сцене с этим размером по умолчанию. После того как вы промасштабируете свой ландшафт и установите необходимые размеры, может понадобиться добавить некоторую детализацию. Несмотря на то что бесконечно растягивающаяся плоскость по-своему интересна, велика вероятность того, что в вашей концепции есть какие-то холмы или горы. Давайте нарисуем их.
Рисуем ландшафт Чтобы получить красивые горы и холмы, нам нужно повлиять на геометрию местности. Для этого нужен инструмент Paint Terrain. Вы можете найти кнопку инструмента во второй доступной опции в инспекторе Terrain Objects (рис. 5.9). Параметр Brush, который находится непосредственно под параметрами Tool, представляет собой раскрывающийся список, который изменит функциональность кисти. Чтобы просмотреть список параметров кисти, щелкните по раскрывающемуся списку, как на рис. 5.10.
Блокирование 99
Рис. 5.9. Параметры кисти
Рис. 5.10. Параметры рисования
Мы рассмотрим их точные функции, но перед этим я предлагаю вам уделить немного времени на небольшую практику – создайте удобный для вас ландшафт и поиграйте с различными кистями. Чтобы почувствовать, как кисти ведут себя, нет ничего лучше практики. Подъем или опускание рельефа Этот инструмент будет вашим хлебом насущным при создании ландшафта. В зависимости от размера ландшафта и масштаба, в котором вам нужно внести изменения, размер кисти будет уникальным для конкретных нужд. К счастью, есть отличный индикатор, показанный на рис. 5.11, который позволяет узнать размер и форму ваших изменений, прежде чем вы зафиксируете его и щелкните мышью. При выборе кисти Raising or Lowering Terrain она показывает окно описания, которое объясняет, что, если вы нажмете на ландшафт, кисть поднимет рельеф в соответствии с вашей формой кисти, и, если вы удерживаете нажатой клавишу Shift, щелкая по кисти, это опустит рельеф в соответствии с формой вашей кисти. Если вы попытаетесь опустить свой рельеф на плоских участках, то заметите, что он не опустится ниже 0. Это может быть проблемой, если вы планируете делать углубления в ландшафте, чтобы, возможно, добавить воду; есть способ обойти это, который мы рассмотрим позже в разделе «Настройка высоты».
100 Окружающая среда
Рис. 5.11. Визуализация кисти на разной высоте
Paint holes После выбора инструмента Paint holes и выделения им ландшафта, он будет просто стирать его, поэтому данный инструмент может оказаться не самым полезным инструментом. В дополнение к тому, что он просто создает дыру, он делает неровные края, которые плохо сочетаются с остальной красивой и гладкой частью местности! Но еще не все потеряно! Инструмент идеально подходит в случае, если вам нужно построить пещеру как ландшафт, не предназначенный для работы с вогнутыми (concave) формами, которые могут привести к перекрытию (overlap) вершины ландшафта. Как правило, проектируют 3D-сетки, которые охватывают необходимую систему пещер, а затем помещают ее под ландшафт и вырезают отверстие в ландшафте, чтобы получить доступ в пещеру. Мы рассмотрим 3D-сетки более подробно в следующем разделе, но вот объяснение «проще говоря»: 3D-сетка – это набор вершин, создающих многоугольники (полигоны), которые мы используем для визуализации 3D-пространства. Ландшафт – это просто плоская часть, которой манипулируют; это не земля, которую можно просто копать лопатой. Если вы хотите сделать пещеру в земле, вам нужно будет сделать отверстие, а затем построить сетку под землей. Это оставит некоторые зазубренные края, которые вы можете видеть на рис. 5.12. Вы можете скрыть эти неровные края камнями или другими сетками, ведущими в систему пещер.
Блокирование 101
Рис. 5.12. Пример отверстий до и после
Paint texture Теперь вы, скорее всего, наигрались упомянутыми инструментами, и у вас есть какие-то холмы, плато и другой разнообразный серый материал, связанный с местностью. Возможно, вам захочется добавить красок, и, к счастью, есть простой способ сделать это! Можете найти мозаичную текстуру в интернете или использовать одну из текстур, предоставленных в проекте, для ее настройки. Когда вы впервые выбираете инструмент рисования текстур, нужно создать слои для рисования. Ваши слои ландшафта будут пустыми, пока вы не создадите их, так что давайте займемся этим. В правом нижнем углу слоев местности, как показано на рис. 5.13, есть кнопка Edit Terrain Layers. Там можете либо создать, либо добавить слой. Если вы ранее не создавали слой, можете щелкнуть по параметру Add Layer, но никаких вариантов для его заполнения не будет. Вместо этого давайте нажмем Create Layer. Это вызовет диалоговое окно для выбора текстуры. Советом здесь было бы назвать вашу текстуру так, чтобы вы могли легко ее найти, на тот случай,
102 Окружающая среда если у вас планируется большое количество текстур. Примером этого может быть название ваших текстур ландшафта с префиксом TT_, например TT_Grass для травы. В будущем, когда диалоговое окно открыто, вы сможете ввести TT в строке поиска, и он покажет только текстуры местности. Этот трюк можно использовать во всем проекте, поскольку в большинстве доступных опций есть панели поиска для выбора ассета.
Рис. 5.13. Редактирование слоев для рисования
Когда вы выберете текстуру, будет создан ассет Terrain Layer, который содержит выбранную вами текстуру и некоторые параметры материала. На рис. 5.14 показан пример Terrain Layer.
Рис. 5.14. Пример Terrain Layer
Блокирование 103 Основное свойство, которое вы хотите здесь увидеть, – это Tiling Settings. В зависимости от масштаба игры вам может понадобиться отрегулировать масштабирование, чтобы не видеть тайлы текстуры. Если вы посмотрите на рис. 5.15, то увидите, где текстура повторяется снова и снова. Это выглядит плохо из текущего местоположения камеры, но в игре она будет намного ближе, что позволяет нам предотвратить видимость мозаики. Самая интересная часть этого заключается в том, что все это не привязано к местности, поэтому, если вы внесете изменения в ассет слоя, он сразу же обновит местность. Это удобно с точки зрения быстрой работы и получения представления о том, как вы хотите, чтобы местность выглядела, в то время как вы можете добавить карты нормалей и металлические элементы или элементы сглаживания позже. Второе, что произойдет, – то, что вся местность будет окрашена этой текстурой. Когда вы добавите еще один слой, сможете использовать те же формы и размеры кистей, чтобы рисовать другие слои на местности. Интересно! Работа слоев местности заключается в том, что Unity создает текстурную карту для каждой текстуры, нарисованной на местности. Если у вас есть четыре слоя, каждый слой соответствует четырем каналам в текстуре: красному (R), зеленому (G), синему (B) и альфа (A). Если их пять, местность получает новую текстуру и добавляет ее к другому R-каналу другой текстуры. Поскольку это так, ограничьте каждый тайл четырьмя текстурами из соображений производительности! После того как вы научитесь работать со слоями, лучше протестировать небольшие участки с вашим персонажем, чтобы убедиться, что масштабирование на слое имеет смысл для самого игрового масштаба, помня при этом, что будут другие шумовые факторы, такие как трава, камни, деревья или что-либо еще, что будет размещено в вашей среде.
Рис. 5.15. Проверка силы кисти при рисовании
104 Окружающая среда Настройка высоты При работе с инструментом более низкой высоты вы могли заметить, что он не опускается ниже нулевой точки. Если вы знаете, что будете работать ниже нуля, что очень часто бывает, то рабочий процесс в начале таков: прежде чем вносить какие-либо изменения, установите высоту местности на то, что может показаться подходящим для высоты, в которой вам нужно будет опускаться ниже нуля в единицах. Вам нужно спуститься на 200 единиц вниз? Если это так, установите для игрового объекта ландшафта значение –200 в направлении y, как на рис. 5.16, выделенном красным цветом и цифрой 1. Затем выберите параметр Paint Terrain, а также раскрывающийся список Set Height, как показано на рис. 5.16, отмеченный красным цветом и цифрой 2. После этого значение будет равно -200, поэтому установите его на 0, затем сгладьте его с помощью кнопки Flatten на рис. 5.16, отмеченной красным цветом и цифрой 3.
Рис. 5.16. Установка высоты, позволяющая размещать объекты ниже мирового уровня 0
Таким образом, вы визуально вернете ландшафт к 0, 0, 0 в качестве его местоположения и сохраните смещение на месте, чтобы можно было опустить ландшафт ниже этой отметки. Это очень эффективно для создания болот, пещер и рек. Сглаживание высоты Сглаживание – простой инструмент. Иногда вам может понадобиться просто немного сгладить местность, так как создаваемый на ней шум мог выйти изпод контроля, или нужно сгладить путь, по которому должен идти персонаж
Блокирование 105 игрока, чтобы помочь направить движение персонажа. В качестве простого примера посмотрите на рис. 5.17.
Рис. 5.17. Сглаженная местность
Это несколько экстремальная версия сглаживания, ведь до сглаживания оба холма выглядели одинаково. Вы также можете применить сглаживание шумной кистью, чтобы сгладить неравномерно и придать местности вид эрозии. Штамповка местности Инструмент Stamp используется как 3D-штамп! Если вам нужен конкретный элемент местности, вы создаете карту высот, чтобы «отштамповать» местность. Вы добавляете карту высот в кисти, а затем используете ее на местности. Одним из основных вариантов использования этого инструмента является то, что вы можете получить предварительно созданные карты высот, которые точно хорошо выглядят на местности. Если вы хотите найти красивые горы и холмы, можете найти их в магазине ассетов, и там будут примеры для начала работы. Это резко ускорит процесс. Возможно, это не совсем то, что вам нужно, но каждый шаг – это прогресс.
Отрисовка деревьев Выберете инструмент Paint Trees, у него будут параметры, как показано на рис. 5.18. Чтобы начать, нажмите Edit Trees и добавьте деревья. Вы сможете добавить любую сетку, которая вам нравится, даже если это не дерево! Обратите внимание на предупреждение, показанное на рис. 5.19, которое появляется, если ваша сетка построена неправильно для размещения дерева на местности. Это выглядит так:
106 Окружающая среда
Рис. 5.18. Режим ландшафта Paint Trees
К счастью, в магазине ассетов SpeedTree есть бесплатный ассет, который вы можете легко скачать и получить отличный пример того, как правильно собрать дерево для инструмента Paint Tree.
Детализация Наконец, у нас есть детали для рисования. Здесь вы можете либо добавить детальную текстуру, которая будет отображаться на кваде, либо использовать детальную сетку для создания собственной сетки. На рис. 5.20 пример простой текстуры травы, используемой и нарисованной на плоской местности. Прорисовка таких деталей, как трава, помогает разбить текстуру земли, как показано на рис. 5.20. На эти элементы также могут влиять зоны ветра, являющиеся еще одним компонентом, который можно добавить к объекту местности. Если вы хотите прыгнуть вперед, изучите главу 10 «Звуковые эффекты», где мы добавим окружающие звуки и другие мелкие детали, а также главу 12 «Последние штрихи», чтобы вдохнуть жизнь в местность.
Блокирование 107
Рис. 5.19. Предупреждение об определенном дереве и группе LOD
Рис. 5.20. Трава как детализация
108 Окружающая среда
3D-геометрия Теперь, когда вы настроили свою местность, нужно будет увеличить ландшафт для архитектуры или построить систему пещер. Для этого используется инструмент 3D Digital Content Creation (3D DCC) для создания сеток для вашей среды. Есть еще один вариант создания фазы блокировки – ProBuilder от Unity. В нашем случае мы будем использовать как ProBuilder, так и создавать собственную геометрию для определения кастомной формы, чтобы обозначить определенные архитектурные части среды. Давайте углубимся в то, что ProBuilder и кастомные сетки означают для блокировки вашей среды.
Рис. 5.21. Примеры Unity ProBuilder и кастомных сеток из нашего 3D DCC
ProBuilder Из документации Unity ProBuilder определяется следующим образом:
Блокирование 109 Вы можете создавать, редактировать и текстурировать собственную геометрию в Unity с помощью действий и инструментов, доступных в пакете ProBuilder. Вы также можете использовать ProBuilder, чтобы помочь с дизайном уровней в сцене, прототипированием, сетками коллизий и игровым тестированием. ProBuilder может быстро настроить вашу сцену с коллизирующими поверхностями, чтобы быстро и легко визуализировать вашу среду. Чтобы начать работу с инструментом, мы рассмотрим несколько начальных шагов, где вы можете создать свою собственную сцену для простоты знакомства. Рассмотрим установку, создание форм ProBuilder, редактирование, а затем некоторые часто используемые инструменты в ProBuilder. Установка ProBuilder Чтобы установить ProBuilder, откройте диспетчер пакетов и перейдите в реестр Unity, как показано на рис. 5.22. Выберите ProBuilder, а затем загрузите и установите его.
Рис. 5.22. Диспетчер пакетов реестра Unity
110 Окружающая среда После его установки вам нужно будет зайти в меню Tools, чтобы открыть ProBuilder Window, как на рис. 5.23.
Рис. 5.23. Путь к ProBuilder Window
Это откроет всплывающее окно со множеством опций. Для начала давайте закрепим окно слева от окна сцены. Это личное предпочтение, но нам нравится работать одновременно с ProBuilder и по-прежнему иметь возможность легко выбирать элементы в иерархии. Теперь давайте пройдемся по цветам в меню. На рис. 5.24 мы смотрим на режим Object в ProBuilder, и в этом меню доступны только три цвета, но есть четвертый цвет, который мы рассмотрим в разделе «Общие инструменты ProBuilder». Три цвета, что мы видим в настоящее время, используются особым образом, чтобы упростить работу с поставленными задачами. Они работают следующим образом: оранжевый: оконные инструменты объектов; синий: функции на основе выбора; зеленый: инструменты редактирования сетки, влияющие на всю выбранную форму. Теперь, когда у нас есть представление о том, для чего они нужны, давайте приступим к созданию нашей первой формы. Создание форм в ProBuilder Откройте новую сцену и давайте создадим новую фигуру из меню ProBuilder. Откроется маленькое окно в правом нижнем углу вьюпорта Scene, как показано на рис. 5.23. У вас будет несколько вариантов с точки зрения типа формы, которую вы хотите создать. Мы собираемся выбрать параметр Plane, чтобы у нас было что-то, к чему можно прикрепить наши фигуры. Взгляните на рис. 5.25.
Блокирование 111
Рис. 5.24. Режим Object в ProBuilder
Рис. 5.25. Подменю Create Shape
112 Окружающая среда После выбора плейна щелкните левой кнопкой мыши и перетащите ее в сцену, чтобы создать плейн. Пока не беспокойтесь о его размере; просто создайте, а дальше мы внесем правки. Теперь в иерархии, если плейн еще не выбран, выберите его. В инспекторе давайте установим трансформ на 0, 0, 0, чтобы он центрировался в нашей сцене на 0. Затем перейдите к ProBuilder (Script) и измените размер на 80, 0, 80. Данного размера достаточно, чтобы поиграть с любой формой, которую мы хотим в сцене. После выполнения этих шагов инспектор должен выглядеть примерно так, как показано на рис. 5.26.
Рис. 5.26. Окно свойств положения и формы в инспекторе
После того как плоскость создана, выполним куб. Создайте новую форму и выберите форму куба в интерактивном инструменте в сцене. Щелкните левой кнопкой мыши и перетащите, чтобы придать основе любую желаемую форму. Повторно щелкните левой кнопкой мыши, и тогда вы сможете перетащить куб, чтобы задать его высоту. Завершите создание куба, щелкнув еще раз по той высоте, которую вы хотели бы получить на данный момент. Как только вы это сделаете, давайте добавим к кубу еще одну фигуру – лестницу. Цель состоит в том, чтобы сделать набор лестниц, которые позволят персонажу подняться на вершину вашего куба. Если ваша лестница получилась странной,
Блокирование 113 не волнуйтесь; просто удалите их и повторите попытку, пока не доберетесь достаточно близко. Не беспокойтесь о том, чтобы сделать ее идеальной; далее мы перейдем к редактированию фигур. Редактирование фигур Возможно, когда вы создавали свои лестницы, они получились примерно такими, как на рис. 5.27.
Рис. 5.27. Проблема размещения лестницы
Не то чтобы так хотелось, просто так получилось. К счастью, часть редактирования в ProBuilder очень мощная. Если вы наведете указатель мыши на лицевую сторону лестницы, должна появиться синяя стрелка вверх, вниз, влево и вправо, соответствующая центральному квадрату этой лицевой стороны, как показано на предыдущем рисунке. Если у вас нет контроллера при наведении курсора, это означает, что вы, возможно, отменили выбор лестницы, и ProBuilder считает, что вам больше не нужно редактировать базовую форму. Это можно легко исправить, выбрав другой элемент в сцене, а затем повторно выбрав лестницу. В инспекторе и скрипте ProBuilder есть кнопка Edit Shape. При выборе этого параметра вы снова получите доступ к основным функциям редактирования. Щелчок и перетаскивание среднего квадрата позволит вам переместить эту грань фигуры. Щелчок по стрелке изменит ориентацию всей фигуры. Это очень удобно для работы с нашей лестницей. Желтая стрелка, выделенная на рис. 5.27, направит форму к задней части лестницы, в результате чего самая
114 Окружающая среда верхняя лестница окажется напротив куба. Это то, что мы хотели, поэтому мы выбрали его, а затем использовали инструмент и переопределили форму, чтобы лестница, по нашему мнению, выглядела лучше всего в этом примере. Несмотря на то что эти инструменты достаточно эффективны для блокировки, мы можем пойти немного дальше и поговорить об инструментах манипулирования компонентами. Общие инструменты ProBuilder Мы создали несколько фигур и отредактировали их общее состояние и форму, чтобы получить основные строительные блоки структуры. Чтобы получить еще несколько средних фигур, нам нужно поработать над компонентами этих фигур. Возвращаясь к главе 1, структура компонентов трехмерных объектов – это вершина, ребро и плоскость. Когда ProBuilder установлен, они обозначаются маленьким значком в верхней части вьюпорта сцены, как показано на рис. 5.28.
Рис. 5.28. Инструмент Component Selection
Слева направо у нас есть Объект, Вершины, Ребра и, наконец, выбор Грани. Если вы выберете один из них, сможете выбрать те типы компонентов формы ProBuilder, которые вы выбрали в иерархии. Это также изменит доступные параметры в наборе инструментов ProBuilder. Сравните рис. 5.29 и рис. 5.24, и вы увидите, что мы добавили красный цвет к нашим доступным параметрам, а другие цвета также претерпели изменения в своих параметрах. Доступные красные параметры работают с каждым компонентом. В данном случае мы выбрали манипулятор вершин; т. е. сейчас параметры инструментов компонентов работают конкретно с вершинами. То же самое относится и к выбору ребер или граней. Выделите некоторое время и изучите их, чтобы увидеть доступные параметры для каждого компонента. Когда вы доберетесь до граней, давайте остановимся на мгновение, так как есть один инструмент, который используется крайне часто, – инструмент Extrude. Выдавливание (extrude) плоскости дублирует вершины и сохраняет плоскости соединенными, чтобы выдавить вашу геометрию из выбранных плоскостей. Это невероятно мощный и быстрый инструмент для создания множества форм или добавления деталей. Мы выполним два примера, чтобы дать представление о том, как это работает. Это выдавливания (extrusions) и insets. Чтобы сделать выдавливание, выберите параметр компонента плоскости в верхней части окна сцены, а затем – грань блока, который у нас есть рядом с лестницей. Нажмите клавишу W, чтобы перейти к инструменту трансформа. Удерживая клавишу Shift, щелкните левой кнопкой мыши и перетащите стрелку вверх. Вы должны получить выдавливание вверх! Это должно выглядеть так, как показано на рис. 5.30 ниже.
Блокирование 115
Рис. 5.29. Параметры меню компонентов
Рис. 5.30. Инструмент Extrude
Выдавливание – это верхний прямоугольник, вытянутый из выбранной грани. Теперь вы можете манипулировать плоскостью и придать ей любую форму.
116 Окружающая среда Мы не большие поклонники углов 90° во всех элементах, поэтому мы нажали R и отмасштабировали ее от середины, чтобы придать немного индивидуальности. Insets представляют собой определенный набор экструзии, чтобы вернуть грань в исходную форму, что отлично подходит для быстрого добавления деталей. Для этого выделите грань на боковой стороне бокса, обращенной к камере, нажав букву R для инструмента масштабирования и удерживая нажатой клавишу Shift, щелкнув левой кнопкой мыши по кубу в центре контроллера, чтобы отмасштабировать одновременно со всех сторон. После этого нажмем клавишу W, чтобы вернуться к инструменту преобразования, и, снова удерживая клавишу Shift, потяните синюю стрелку, чтобы создать инсет. Рисунок 5.31 ниже включает в себя оба шага для создания вставки на той же грани, как это было предложено.
Рис. 5.31. Пример инсета
Поэкспериментируйте с инструментами для каждого из компонентов. Вы обнаружите, что они помогают ускорить рабочий процесс, чтобы вы могли заблокировать как можно больше и как можно быстрее. Может случиться ситуация, что нужна определенная форма, но вы не можете достичь ее с помощью инструментов ProBuilder. В этом случае вам может понадобиться внешний инструмент, такой как Autodesk Maya или Blender, чтобы добиться необходимой формы. Мы склонны называть эти объекты готовыми формами.
Готовые базовые формы Если у вас есть хорошее представление о формах для вашей среды, вы, возможно, уже настроили части среды, которые связаны друг с другом в нужном масштабе. ProBuilder очень хорошо работает с базовыми формами, но вам может понадобиться некоторая архитектура, специфичная для вашей игры. Если у вас есть возможность, может быть, проще просто создать ее и использовать меш вместо использования ProBuilder. В некоторых случаях у вас могут быть уже созданные ранее формы, которые будут работать так же хорошо, как ProBuilder, и все, что вам нужно сделать, – это импортировать их в Unity и разместить там, где вы хотите. Это может ускорить этап блокировки.
Итерирование 117 В наших сценах вы увидите, что мы используем все три способа (Terrain, ProBuilder и готовые формы из другого софта), чтобы собрать цельную интегрированную сцену. В первую очередь это связано со скоростью, с которой наш художник мог создавать определенные модели в DCC по сравнению с ProBuilder. Однако иногда базовый блок, который был выдавлен, – это все, что нам нужно, чтобы сцена имела смысл для блокирования.
Итерирование Процесс итерации проходит через этапы работы, чтобы достичь достаточного состояния и перейти к другим частям игры. Мы будем следовать рис. 5.32 ниже как простой мантре. Это высокий уровень процесса, и после того, как вы пройдете его несколько раз, вы создадите больше шагов для себя и своей команды. А пока давайте проработаем основные моменты. Оценка
Анализ
Исследование
Да Нет Концепция
Блокирование
Ответили на вопросы?
Нет Уточните Да
Ответили на вопросы?
Протестируйте
Да
Больше вопросов?
Нет
Переходите дальше
Рис. 5.32. Процесс итерации
Мы уже прошли через несколько блоков в описанном выше процессе, таких как зарождение идей и блокирование. Нам просто нужно пройти уточнение и тестирование. Уточнение – это действие, которое вы предпринимаете, чтобы максимально приблизиться к необходимому ответу на действие на детальном уровне. Примером этого в отношении нашего проекта является вопрос о том, насколько близка каждая головоломка культуре Мивари. Нам нужно было усовершенствовать это от базовых фигур до архитектуры и убедиться, что сама головоломка имеет смысл для стиля в механике, что мы рассмотрим в главе 6 «Взаимодействия и механика». Если ответ на детализированном уровне имеет смысл, переходите к тестированию. Во время тестирования вы будете проходить игру в общих чертах. Если можете, проверьте ощущения от игры, пройдясь вокруг, и посмотрите, есть ли какие-либо другие вопросы на высоком уровне, которые могли возникнуть в процессе уточнения. И вот здесь можно потерять счет времени. Вам нужно четко понимать, что означает «достаточно хорошо» для вашей игры. Одна из самых сложных частей разработки игр – это когда вы должны понимать, что релиз игры важнее, чем доведение ее до совершенства. Будьте бдительны в своем деле, чтобы заставить продукт выглядеть и чувствовать себя достаточно хорошо для релиза, а затем двигаться дальше. Вам еще многое предстоит пройти! В этой главе об окружающей среде мы говорили только об этапе блокирования, так как на это очень важно выделить некоторое время и прочувствовать игру. Не бойтесь совершать ошибки и вносите изменения в соответствии с ва-
118 Окружающая среда шими представлениями, отзывами играющих или от одного из ваших друзей, которым вы доверили тестирование. После того как вы дойдете до точки, где, по вашему мнению, каждая секция достаточно хороша, можете начать импортировать окончательные меши. Как только это будет завершено, выполните еще один запуск игры, так как вы можете увидеть изменения по сравнению с окончательными мешами, которые не были видны ранее с блокированными мешами. Перемещаясь по уровню и по нескольким частям сцены, внимательно следите за взаимосвязью. Как только передвижение по уровню становится приятным, вам нужно больше углубляться в механику самой игры и больше заниматься развитием, помимо искусства. В будущем возникнут дополнительные вопросы. Серьезное отношение к итерациям в первую очередь заложит прочную основу для проработки, когда вы перейдете к следующим деталям своей игры.
Заключение В данной главе мы рассмотрели большое количество инструментов Unity. Не торопитесь сразу досконально изучить инструменты Terrain и ProBuilder, достаточно хорошо понять, как они работают. Из этой главы вы узнали о нескольких инструментах для создания среды. Мы потратили время, чтобы объяснить, как итерировать весь этот процесс, с целью получить четкое представление о структуре вашей среды. Вы узнали, как создать концепцию при помощи дизайн-мышления. Затем вы взяли концепцию и начали инсценировать каждый ее раздел концепции, прежде чем, наконец, собрать среду воедино и сделать итерацию, чтобы получить четкое целостное представление. Далее мы рассмотрим механику игры, чтобы она соответствовала среде. Имейте в виду эту главу при размещении взаимодействий для механики, так как в процессе разработки будет больше итераций.
6 Взаимодействия и механика Теперь, когда у нас есть персонаж с базовыми движениями и среда, с которой можно работать, давайте посмотрим, каким образом этот персонаж должен взаимодействовать с этой средой. Unity позволяет нам использовать C# для построения логики вокруг игровых объектов, с которыми может взаимодействовать игрок. Это основа игрового дизайна, позволяющая рассказать историю или передать опыт через реальное взаимодействие. В этой главе вы узнаете больше о конкретных взаимодействиях и механизмах, которые можно реализовать с помощью Unity. Мы рассмотрим:
игровые циклы, инструментарий механик, взаимодействия в рамках нашего проекта, лестницы, головоломку с кольцами, ограниченное пространство, Interactive volumes, дизайн и реализацию.
Игровые циклы В видеоиграх есть уникальная концепция, называемая игровым циклом (game loop). Как вы могли догадаться, это цикл механик, которые выполняются на протяжении всего опыта. Сам игровой цикл может быть очень коротким, как, например, многопользовательский командный бой насмерть в Call of Duty. Цикл выглядит примерно так, где цель состоит в том, чтобы убить как можно больше врагов, прежде чем убьют вас: 1) убить врагов; 2) умереть и возродиться. Это, конечно, еще не все, и, если вы профессиональный игрок в Call of Duty, можете подумать, что это чрезмерное обобщение игрового процесса. В конечном счете, однако, это действительно так в 90 % случаев. Теперь давайте посмотрим на игровой цикл Minecraft:
120 Взаимодействия и механика 1) собирать ресурсы днем; 2) строить днем; 3) выжить ночью. Проще говоря, есть определенные обстоятельства, которые выходят за рамки этого цикла, такие как лианы и дождь днем, снижающие уровень освещенности и создающие видимость ночи. Предположим, что эти два фактора не являются частью данного исследования. Однако это интересно, так как такой цикл особенно сложен. Под этим подразумевается, что выживание не всегда происходит в одном и том же цикле. Большую часть игры составляет пункт 1, затем 2. Только ночью пункт 3 – выживание ночью – становится значительной частью игрового процесса, визуально представленного на рис. 6.1. Основной цикл игры должен быть максимально кратким.
Ночные или дневные враги Собрать ресурсы
Крафтить/Строить
Выжить
Рис. 6.1. Игровой цикл Minecraft
Взгляните на свои любимые игры и разложите их основные игровые циклы. Вы можете обнаружить, что существуют слои игровых циклов. Иногда это называют метапрогрессией. В игре Hades игровые циклы следующие: 1) (необязательно) поговорить с NPC; 2) (в лобби) выбрать навыки для улучшения; 3) (в лобби) выбрать оружие для следующего похода; 4) (в игре) бой; 5) (в игре) зарабатывать валюту для улучшений в лобби; 6) (в игре) улучшать для упрощения этого прохождения; 7) умереть и возродиться в лобби. Метапрогресс происходит на втором пункте. Увеличение базового здоровья и урона немного облегчают дальнейшее продвижение. Это распространенный фактор в жанрах rogue-like, где игровой опыт сосредоточен на мастерстве навыков и развитии игры вплоть до смерти. Вы заметите, что в цикле Call of Duty мы не упомянули метапрогресс, хотя в этой игре он сильный. Это связано с тем, что метапрогрессия, по сути, является косметической. Вам не нужно ничего менять между матчами в Call of Duty. Любое снаряжение, полученное в Call of Duty, будет таким же, как снаряжение другого игрока с теми же модификациями. Если вы поставите игрока, наигравшего 1000 часов, против игрока с такой же экипировкой, все сводится
Инструментарий механик 121 только к мастерству самих игроков. Однако в Hades вам нужно тратить очки на улучшения, чтобы пройти игру. Эти циклы интересны, но нам нужно некоторое время, чтобы углубиться во взаимодействия, из которых состоят эти циклы. В следующем разделе мы рассмотрим широкий набор игровых механик по отдельности.
Инструментарий механик Взаимодействие – это действие, совершаемое с использованием механики. Например, механику использования предмета можно применять, чтобы потянуть за рычаг, нажать кнопку или использовать телефон. Эти три примера – взаимодействия; механика позволяет игроку взаимодействовать с предметами нажатием кнопки. Если бы у нас была возможность взаимодействовать с чем-то исключительно таким образом, у нас было бы очень мало жанров для игр. К счастью для нас, существует широкий мир механик, которые мы можем использовать для взаимодействия. Используя взаимодействия, можно создавать потрясающие впечатления! Мы подумали, что в начале этой главы было бы неплохо предоставить список механик и некоторых взаимодействий, которые используются из этой механики. Мы не сможем пройтись по всем механикам, но рассмотрим некоторые основные понятия, чтобы получить хорошее представление о базе механики. Здесь мы дадим понимание механики и того, как ее можно рассматривать. Если это вас интересует, найдите время, чтобы прочитать несколько разных авторов по этому вопросу, поскольку их взгляды на механику могут отличаться от наших. Наш взгляд на механику состоит в том, что она представляет собой слои эмпирического движения. Существуют основные концепции, которые можно накладывать друг на друга для формирования взаимодействия. К ним относятся: управление ресурсами, риск vs вознаграждения, пространственное воображение, коллекция, исследование, ограничения. Читайте дальше, чтобы получить представление об этих модульных основных концепциях игрового дизайна.
Управление ресурсами Вы можете знать об этом как об основной механике стратегических игр в реальном времени (RTS – real-time strategy). Starcraft, Age of Empires и Total Annihilation – примеры популярных игр, ориентированных на управление ресурсами. Идея здесь заключается в том, что есть ограниченные ресурсы, которые вам нужно собрать и потратить на то, что может помочь вам выиграть. Это могут быть солдаты для армии или эксперименты, чтобы сделать ваших солдат сильнее. Сценарий, не связанный с боем, – это строитель города. Вам нужно следить за жителями вашего города и строить, чтобы сделать их счастливыми, и вы управляете поступающими от них деньгами.
122 Взаимодействия и механика
Риск vs вознаграждения Эта механика используется во многих боевых играх. Обычно это дается в виде кулдаунов (cooldown). Наверняка вы захотели использовать свой ульт (ultimate) прямо сейчас, чтобы продемонстрировать пример из популярной игры League of Legends? Это может уничтожить врага и дать вам большое преимущество. Тем не менее это также может поставить вас в невыгодное положение, если промахнетесь, потому что враг будет знать, что у вас на одну способность меньше. Это концепция риска и вознаграждения. Простейшая пример этого – в Super Mario Bros. Стоит ли пытаться добыть труднодоступные монеты? Вам нужны эти очки для дополнительной жизни, но в то же время есть яма, в которую вы можете упасть, если прыгнете неправильно.
Пространственное воображение Это часто встречается в шутерах от первого лица. Call of Duty и Overwatch используют это несколькими способами. Во-первых, у вас есть пространственное представление о противнике на экране. Вы должны иметь возможность поместить курсор туда, где они находятся на экране, чтобы стрелять в них. Во-вторых, есть пространственное понимание всей карты. Если вы не ориентируетесь в пространстве на карте, вас легко могут застать врасплох. Это является базой платформерных игр. Понимание своего положения в пространстве 2D и способность ловко маневрировать – вот суть игры в любом экшн-платформере. Игра Celeste в полной мере использует это, предоставляя игроку схемы жесткого контроля, которые каждый раз двигаются так, как вы ожидаете. Движение настолько хорошо слажено, что, когда вы делаете ошибку, чувствуете это как свою вину. Интересно: если у вас есть погрешности управления в игре, которая требует точнейшего управления, игрок может почувствовать себя обманутым игрой и может перестать играть. Это нежелательно!
Коллекция Есть тут любители ККИ? Это в названии! Коллекционная карточная игра (Collectable Card Game). Magic: The Gathering, Hearth-stone, Yu-Gi-Oh! и Pokémon – вот лишь несколько примеров. Хотя эта механика не бросается в глаза, концепция коллекционирования используется во всех играх. Можно собирать навыки, оружие, рукописи, доспехи, и этот список можно продолжать бесконечно. Люди любят собирать вещи. Возможно, вы хотите, чтобы каждая карта в сезоне открывала достижение. Это двойной сбор, так как вы хотите получить карты, но вы также хотите собирать эти достижения. Возможно, вы хотите собрать все рукописи в игре, например в Mass Effect, где история игры основана на взаимодействии как можно с большим количеством уникальных вещей, а ваш журнал обновляет записки, в которых хранится информация об уникальных предметах, персонажах, расах, историях и т. д.
Исследование Исследование – это способность заниматься расследованием, чтобы установить факты и правила для вашего окружения. Мы можем использовать концеп-
Проектирование и реализация 123 цию исследования несколькими уникальными способами. Одна мысль состоит в том, что именно игрок должен проводить исследование, а не персонаж. Это означает, что игрок видит окружение за персонажа. Из-за этого игрок может узнать о предметах и вещах, которые могут быть за пределами знаний персонажа. Мы, как дизайнеры, можем использовать эти знания и легче передавать информацию игроку, определенным цветом выделяя взаимодействующие объекты или уступы, на которые можно взобраться. С другой стороны, понятие исследования может относиться к самому персонажу. Персонаж в игре исследует и делает для себя что-то новое в своем мире и становится сильнее, расширяя свое сознание как физически, так и умственно. Это может показаться похожим на управление ресурсами; однако, если это связано с передачей знаний от персонажа к игроку или вещами, которые по своей сути извлекаются из игры, это следует рассматривать как исследование.
Ограничения Под давлением рождаются алмазы. Вместо того чтобы быть механикой самой по себе, ограничения можно рассматривать как модификаторы других/всех механик, но нам нравится выделять их как отдельную механику, поскольку не каждое взаимодействие требует наличия жестких ограничений. Могут быть всеобъемлющие ограничения, влияющие на общий игровой процесс. Например, добавление таймера в игру в целом является ограничением. Или же когда игроку дается только три жизни перед окончанием игрового сеанса. В ККИ вы можете увидеть, что колоды имеют жесткое ограничение. Это ограничивает количество возможных ходов. Когда вы поймете, как эти механики сочетаются друг с другом для создания взаимодействий для определенного опыта, вы сможете разработать полноценную механику. То, как все эти части сочетаются друг с другом, составляет суть механики и дизайна взаимодействия. Давайте выделим немного времени, чтобы рассмотреть некоторые нюансы.
Проектирование и реализация По-хорошему, нам нужно разбить причины для механик и взаимодействий, которые мы собираемся использовать. В общем, нужно свести к минимуму количество механик в игре, распространяя их использование на множество уникальных взаимодействий. Mega Man – отличный пример минималистичной механики с элегантным использованием для небольших вариаций. Передвижение, прыжки и стрельба – единственное, о чем вам придется задумываться. После победы над врагами вы получаете различные способности или навыки стрельбы, но по-прежнему будете использовать ту же кнопку, чтобы включить механику стрельбы. Эта механика поддерживается нажатием одной кнопки вплоть до Mega Man 4; когда персонаж может зарядить свое оружие, и обозначение кнопки меняется, чтобы адаптироваться к изменению навыка. Интересная мысль: гейм-плей включает в себя очень ограниченное количество изменений механики, вместо этого просто меняется графика и повествование. Когда вы начнете разрабатывать эту часть своей игры, подумайте о наименьшем действии, которое должен предпринять ваш игрок для прогресса, и разбейте его на мельчайшие компоненты.
124 Взаимодействия и механика Если вы думаете о разработке игры, которая в значительной степени ориентирована на сражения, нужно задать себе несколько вопросов: каков боевой стиль? соответствует ли боевой стиль исторической тематике окружающей среды? совпадает ли боевой стиль с персонажем или контрастирует с его моралью? Как ответы на все вышеперечисленные вопросы соотносятся с эмоциональным опытом, который вы просите у игрока? Эти вопросы ни в коем случае не являются исчерпывающими. Каждый из вопросов должен вести к дальнейшему прояснению игровой механики и формировать впечатления, которые вы хотите получить от своих игроков. Слишком легко попасть в ловушку комфорта жанра со своей игрой. Если вы обнаружите, что проектируете что-то и думаете про себя: «Так это всегда делалось», вам нужно оценить это взаимодействие. Шутеры от первого лица (FPS – First-person shooter) являются отличным примером этого из-за ограничений точки зрения от первого лица. Есть один крупный уникальный аутсайдер, который очень хорошо зарекомендовал себя в пространстве FPS: Half Life. Valve создала шутер от первого лица с механикой головоломок, основанной на физике, и большим упором на повествование. Это было в высшей степени уникальным по сравнению с гиперразрушением «беги и стреляй», которое было в центре внимания предыдущих игр FPS. Поскольку мы говорим о взаимодействиях и механике через призму дизайна, нужно поговорить об игре Undertale. Undertale – это игра, которая начиналась как ролевая игра с низкой графической точностью. Повествование игрового процесса поначалу кажется нормальным; затем происходит бой! Вы быстро изучаете боевую механику, необходимую для победы, и боевой гейм-плей доставляет удовольствие. Однако причинение вреда вещам не всегда является тем, на чем вы хотите сосредоточиться. Существует риск подорвать ожидания игроков, когда игра просит, чтобы ваш персонаж причинил боль людям, с которыми у персонажа может быть эмоциональная связь в игре. Эта эмоциональная разница выявляется, чтобы показать использование стандартного таким образом, чтобы перевернуть ожидания игроков с ног на голову. Это доступно только в том случае, если вы, дизайнер, хорошо изучаете и знаете игровой дизайн. Всю эту главу можно легко посвятить разговорам о дизайне механик и взаимодействий в других играх. Вместо того чтобы разбирать множество игр, давайте поработаем над нашим собственным проектом и рассмотрим несколько простых примеров механик и взаимодействий, которые можно использовать в них. В разделах мы также будем рассматривать реализацию различных игровых дизайнов. Говоря метафорически, мы надеемся, что разложим кусочки головоломки во взаимодействии сверху донизу, чтобы показать вам, что разработка игры заключается в разбиении каждого кусочка, не забывая при этом о всей картине. Один из советов при чтении этих разделов заключается в том, что реализации этих игровых взаимодействий не высечены на камне и они не являются единственным способом использования этих взаимодействий. Это всего лишь примеры нашего подхода. Попробуйте представить, как вы могли бы изменить дизайн каждой части.
Наш проект 125
Наш проект Мы создаем приключенческую 3D-игру-головоломку. Наша начальная механика будет использовать исследования в качестве основного компонента. Позже в главе 7 в отношении нашего дизайна телекинеза мы наложим на него пространственное осознание. С этим пониманием мы будем строить наш игровой цикл. Чтобы сделать игру стоящей, можно определить игровой цикл следующим образом: поиск подсказок в окружающей среде, решение головоломки из подсказок. После определения игрового цикла и понимания того, что мы фокусируемся на исследованиях как на основной механике, теперь нам нужно создавать взаимодействия для получения опыта. Чтобы начать взаимодействие, мы проработаем пару простых действий, не связанных с физикой. Персонаж Мивари должна иметь возможность взаимодействовать с окружающей средой, чтобы решать головоломки и входить в области с целью добраться до места назначения. Тематически окружение представляет собой руины прошлого ее расы. В нашем демонстративном вертикальном срезе Мивари столкнется с множеством экологических загадок, в которых ей нужно будет исследовать свое окружение и преодолевать препятствия. Игрок в этой игре, ведущий Мивари своим контроллером, должен обратить внимание на детали окружающей среды и научиться решать головоломки окружающей среды. Первое взаимодействие, с которым столкнется персонаж, – это лестница. Давайте углубимся в ее дизайн, чтобы получить реальное представление о том, что потребуется остальным взаимодействиям с точки зрения определения.
Лестницы На этом демонстративном уровне присутствует набор лестниц, вплетенных в окружающую среду, по которым должен пройти персонаж. Понимание того, что эти лестницы сообщают игроку, помогает установить фундаментальное ощущение возможности раннего взаимодействия, а это означает, что ваше окружение будет основным направляющим фактором для игроков, перемещающихся по уровню. Давайте поработаем над созданием этого начального взаимодействия, так как это первый опыт, частью которого игрок действительно становится.
Проектирование Когда игрок входит в игру, Мивари входит из леса в пещеру, которая кажется обычной. Двигаясь в пещеру, вы попадаете в небольшой коридор, он ведет к отверстию с крутым склоном, по которому невозможно подняться. Есть два бассейна, по одному с каждой стороны этого склона. С каждой стороны есть рычаги. Все, что должно произойти, – это то, что с каждым уровнем нужно взаимодействовать. На рис. 6.2 показано, как это будет происходить.
126 Взаимодействия и механика
Рис. 6.2. Обзор начального взаимодействия, лестница
При первом столкновении с открытым космосом вы можете почувствовать удивление. Вот пещера с рукотворными чертами. Вдалеке виднеется подобие двери и тропинка, ведущая к ней. Пробираясь к двери, вы заметите, что путь впереди становится очень крутым. Прогуливаясь, вы исследуете местность и находите рычаги. Эти рычаги приведут к лестнице, чтобы вы могли подняться к двери, окруженной тем, что выглядит как руины. Здесь должен быть простой дизайн среды с «накапливанием света». Это когда вы добавляете освещение в область, чтобы привлечь внимание игрока. Мы склонны ходить в места с большим количеством света. Поэтому нам нужно, чтобы игроки взаимодействовали с определенными моделями внутри сцены. Чтобы сделать это заметным для игрока, вы добавляете возможности игрока к этим обозначенным моделям. Например, когда вы подойдете достаточно близко к рычагам, они будут слегка подсвечены. Внезапно появится всплывающая подсказка, показывающая, какую кнопку нажать для взаимодействия с ней. Взаимодействие с обоими рычагами создает щелкающий звук. Если отойти от вашей текущей камеры, будет воспроизводиться небольшой кинематографический ролик, показывающий, как лестница поднимается и перемещается на место. С этого момента вы можете подняться по лестнице в зону колец. Кольца станут начальной головоломкой с исследованиями окружающей среды. У нас есть представление о том, как это должно быть реализовано, но реализация всегда вызывает некоторые затруднения. Нам нужно применить все на практике, чтобы увидеть, работает ли дизайн. Давайте войдем в Unity и проверим!
Реализация Во-первых, нужно что-то, с чем мы можем взаимодействовать. Давайте немного разберем это. В этой реализации есть три пункта. Для работы с этими
Наш проект 127 двумя элементами нам нужен блок взаимодействия, блокиратор лестницы и менеджер. Блок взаимодействия Мы знаем, что у нас есть две точки взаимодействия, с которыми нужно выстроить коммуникацию, чтобы заставить лестницы работать. Это означает, что мы должны создать триггер, который можно использовать более одного раза. На кубе должен быть бокс-коллайдер, так как мы будем использовать коллизию для настройки состояний. Теперь рассмотрим некоторый код. Как и в главе 4 , мы не будем изучать каждую строку кода – рассмотрим код более подробно только в том случае, если он новый или по какой-то причине мы вносим изменения в ранее объясненный код. Переходим к InteractionTrigger.cs, который можно найти в папке Assets/ Scripts на GitHub проекта. Если вы еще не нашли GitHub, обратитесь к началу книги за инструкциями о том, как это сделать. При реализации чего-то в первый раз могут быть некоторые ключевые области, которые сложно спроектировать, поэтому рекомендуется поработать с визуальным кодом отладки, чтобы облегчить вам задачу. Мы хотим реализовать простой куб, с которым можно будет взаимодействовать при входе в него, а также визуальное обозначение взаимодействия с помощью нескольких цветов. public Color idleColor = new Color(1f, 0f, 0f, 0.5f); public Color occupiedColor = new Color(1f, 1f, 0f, 0.5f); public Color interactColor = new Color(0f, 1f, 0f, 0.5f);
Определяем их в начале для возможности ссылаться на них, когда будем определять состояния чуть позже. Мы используем действие ввода interact с системой ввода, которую определили в главе 4. В этом случае нам нужно обратить внимание на этот ввод, поэтому мы ставим его на Update. void Update() { interactPressed = interactInput.action.ReadValue() > 0f; }
Вход здесь либо 0, либо 1, но мы хотим использовать его как логическое значение. Это позволяет делать простые проверки if, когда мы хотим изменить состояние. Для этого спрашиваем, если значение больше 0. Если кнопка взаимодействия с назначенным знаком нажата, значение равно 1, что устанавливает для interactPressed значение true; в противном случае – false. В следующих разделах мы используем некоторые новые методы MonoBehaviour. Это OnTriggerEnter, OnTriggerStay и OnTriggerLeave. Как следует из названий, эти методы полезны для работы с состояниями столкновения, когда что-то входит, остается или покидает поле столкновения. Начнем с OnTriggerEnter. Это нужно только для установки цвета поля, чтобы мы могли видеть, что зашли в нужное поле. Это не важно механически, но полезно визуально. Возможно, позже, на этапе полировки, мы захотим создать некоторые частицы или изменить некоторые источники света, чтобы показать игроку, что он находится в области, с которой можно взаимодействовать. А пока давайте просто изменим цвет материала куба для визуальной отладки.
128 Взаимодействия и механика void OnTriggerEnter(Collider other) { MyvariThirdPersonMovement player = other. GetComponent(); if (player != null) { mat.SetColor("_BaseColor", occupiedColor); } }
Здесь происходит следующее: когда игрок соприкасается с полем столкновения, мы проверяем, есть ли у другого компонента, вызвавшего столкновение, сценарий MyvariThirdPersonMovement. Поскольку никакой другой объект, который может столкнуться, не должен иметь этот компонент, это важная проверка. Мы присваиваем это переменной player, а затем делаем небольшую проверку, спрашивая, не равно ли значение player значению null, а затем меняем цвет на выбранный. Теперь нам нужен OnTriggerStay, где игроку дается возможность взаимодействовать с этим ранее столкнувшимся объектом. void OnTriggerStay(Collider other) { MyvariThirdPersonMovement player = other. GetComponent(); if (player != null) { if (interactPressed) { mat.SetColor("_BaseColor", interactColor); OnInteract?.Invoke(); Debug.Log($"Interacted with {gameObject.name}"); if (disableOnInteract) { this.enabled = false; this.GetComponent().enabled = false; } } } }
Все это должно выглядеть аналогично триггеру соприкосновения, пока мы не перейдем к блоку if, ожидающему нажатия кнопки взаимодействия. Когда кнопка взаимодействия нажата, нужно сделать следующее: 1) установить цвет на цвет взаимодействия; 2) вызвать действие; 3) выйти из системы для другой проверки отладки; 4) отключить, чтобы мы не могли взаимодействовать с объектом снова. Ранее мы уже установили цвет, так что это должно показаться вам знакомым. Сейчас же используем цвет взаимодействия, что также имеет смысл! Следующая часть вызывает действие. Наш менеджер будет прослушивать действие, которое будет вызвано. Когда мы доберемся до нашего менеджера,
Наш проект 129 то подробно рассмотрим, как это работает. А пока поймите, что другой элемент будет ждать сигнала, чтобы полностью выполнить действие. Устанавливаем отладку на консоль, чтобы видеть, что происходит в логике. Когда мы удалим цвет отладки, отладка консоли будет нашим руководством, если в будущем возникнет ошибка. Последняя часть – отключить его, чтобы мы больше не могли с ним взаимодействовать. Нам нужно отключить и объект, и коллайдер. Это делается для того, чтобы взаимодействие срабатывало только один раз с каждой стороны. Вот и все! Теперь нужно пройти через блокатор лестницы, прежде чем попасть в менеджер.
Блокатор лестницы Мы знаем, что будет эффект от блокировки лестницы снаружи, и доработали внешний вид этого блокирующего механизма. Пока это отладочный красный блок с коллайдером. Это не проблема, так как известно, как мы хотим, чтобы игра развивалась, поэтому нужно сделать блокатор, который временно просто не позволяет игроку пройти к лестнице. Мы добавим визуальную часть позже в главе 12. Это может быть скользкая плоская лестница, по которой игрок не может подняться, или камень, препятствующий проходу на лестницу, который исчезает после правильного взаимодействия. Здесь нет никаких скриптов. Мы будем выгружать любую логику для этого на менеджера. Одна из причин, по которой нам нужен этот менеджер, заключается в том, что у нас не может быть скрипта на самой лестнице для самостоятельного включения-выключения. Если у вас есть отключенный GameObject, скрипты не смогут быть активированы без какого-либо внешнего объекта с его ссылкой на отключенный GameObject. Так что нам нужно сделать это с помощью менеджера. Менеджер взаимодействий Сшивание скриптов намного проще, если у вас есть родительский объект для интерактивных элементов в своего рода менеджере. В редакторе это часто делается путем создания префаба, в котором родительский префаб содержит скрипт для хранения состояния взаимодействия. Здесь мы делаем так, чтобы лестницу нельзя было включить, пока не будут нажаты обе кнопки. Это было бы сложно сделать без GameObject, знающего состояние каждого элемента. Приступая к коду, мы, как обычно, определяем наши общедоступные переменные и переменные класса для настройки, а затем переходим ко второй части событий, о которых мы говорили в разделе «Блок взаимодействия». В секциях Awake и OnDestroy нам нужно обработать прослушивание событий. void Awake() { leftTrigger.OnInteract.AddListener(OnLeftTriggerInteract); rightTrigger.OnInteract.AddListener(OnRightTriggerInteract); } void OnDestroy() { leftTrigger.OnInteract.RemoveListener(OnLeftTriggerInteract); rightTrigger.OnInteract.RemoveListener(OnRightTriggerInteract); }
130 Взаимодействия и механика Мы определили каждый из наших триггеров публично, и у них обоих есть свои собственные события. При Awake мы слушаем событие OnInteract, и, если оно вызывается, запускаем функцию, которая является аргументом слушателя. В данном случае это OnLeftTriggerInteract для левой стороны. Аналогично называется правая сторона. Мы подробно рассмотрим только левую сторону, так как правая сторона очень похожа. void OnLeftTriggerInteract() { leftTriggerFired = true; if (rightTriggerFired) { stairsRaised = true; OnStairsRaised?.Invoke(); stairsBlocker.SetActive(false); Debug.Log("RAISE STAIRS HERE"); } }
Если срабатывает левая сторона, мы немедленно устанавливаем для свойства leftTriggerFired значение true. Это проверка, была ли уже активирована правая сторона. Если этого не было, то ничего не происходит. Если это так, то мы установим для stairRaised значение true, вызовем другое действие, установим блокатор лестницы в неактивный режим GameObject и выведем строку из журнала для последующей отладки. Действие OnStairsRaisedUnityAction сработает, но к нему пока ничего не привязано. После того как мы закончим эту область и доработаем, что именно нам нужно, мы дополним это действие. Интересно, что эта настройка позволяет игроку без проблем начинать с любой стороны. Это также настраивает на будущее развитие. Нам не нужно все излагать, но важно иметь представление о том, какова общая идея, чтобы можно было соответствующим образом написать архитектуру. На этом текущая реализация головоломки с лестницей завершена. Теперь, когда Мивари поднялась по ней, нужно решить нашу первую главную головоломку – кольца.
Кольца Пройдя по лестнице, мы сталкиваемся с дверью и кольцами. Именно дверь означает первый повествовательный ответ на загадку, а не освещение, привлекающее их к определенной области. Загадку можно будет разгадать только в том случае, если вы обратите внимание на изображение на двери и соотнесете его с головоломкой. Давайте разберем дизайн головоломки колец.
Проектирование Первая головоломка, которую игрок должен будет решить, – это кольца. Когда вы доберетесь до платформы головоломки, ваше ожерелье оживет, т. е. и ожерелье, и средняя колонна будут светиться легким синим цветом, а затем угаснут. На двери будет старая надпись о том, как должны выглядеть столбы, если с ними правильно действовать.
Наш проект 131 Игроку нужно сдвинуть столбы в кольцах, чтобы они соответствовали изображению небесных тел, найденному на двери. Это позволяет использовать несколько уровней исследования и взаимодействия в рамках небольшой сцены. Из подсказок, данных ранее, игрок уже знает, что в окружающей среде есть взаимодействие, и небольшой контур укажет кнопку, которую нужно нажать, чтобы взаимодействовать со столбами. Сбор новой информации будет заключаться в том, чтобы форма изображения в двери соответствовала форме земли. На рис. 6.3 показан концепт области. Большая пустая область впереди – это дверь. Столбы находятся внутри кругов за пределами среднего столба. В общей сложности три кольца.
Рис. 6.3. Кольца и загадка исследования окружающей среды
Прохождение этой головоломки откроет дверь, но время не пощадило эту дверь или область вокруг руин в целом. Когда дверь пытается открыться, в коридорах, ведущих дальше в пещеру, рушатся обломки. Мы воспользуемся этой возможностью, чтобы проработать еще одно простое взаимодействие – перемещение в ограниченном пространстве.
Реализация Мы много думали о том, как бы это все собрать. Две колонны по обе стороны от каждого кольца. Всего три кольца. Нам нужна была определенная конфигурация, которая также соответствовала бы нашим представлениям о дизайне созвездия. Другая проблема заключается в том, как поступить с Мивари, перемещающей эти вещи. Сначала мы думали толкать и тянуть, но, чтобы упростить задачу, мы использовали только толкание. Это позволило нам беспокоиться только о вращении в одном направлении, а также о вырезании анимации. Мивари – некрупный персонаж, и действие вытягивания не имеет особого смысла. Нам нужно придумать два скрипта. Первый будет похож на тот, с которым мы
132 Взаимодействия и механика работали ранее для визуальных объемов. Мы будем использовать его, чтобы определить, у какого столба находится Мивари, что скажет нам, в какую сторону повернуть столб. После того как мы повернули его в правильное положение, нам нужен менеджер головоломки, чтобы знать, где изначально разместить столбы, как выглядит значение правильного поворота и как справиться с окончанием головоломки. Давайте сначала пробежимся по-простому и посмотрим на триггерные значения головоломки. Триггеры головоломки Сейчас будет довольно просто. Нам нужен бокс, которым мы будем менять цвета для отладки, так же как ранее делали для лестницы, а затем нужно иметь несколько вариантов свойств в инспекторе, которые мы можем выбрать до начала игры. Эти варианты (внешнее, среднее и внутреннее) будут определять, на каком кольце они расположены и в каком направлении они должны двигаться. Направление против часовой стрелки или по часовой стрелке. Несмотря на то что мы видели это раньше, в данной реализации изменения цвета мы немного откорректировали доступ головоломки к методу этого класса. public void SetColor(Color color) { meshRenderer.material.color = color; }
Здесь следует обратить внимание на модификатор общего доступа. Он принимает цвет. Помните об этом, когда будем делать менеджер скрипта головоломки. Далее есть два определенных перечисления (enum). Мы поместим их оба следом: FirstPuzzleTriggerType и FirstPuzzleTriggerDirection. public enum FirstPuzzleTriggerType { Outer = 0, Middle, Inner } public enum FirstPuzzleTriggerDirection { Clockwise = 0, CounterClockwise }
Мы сделали общедоступные перечисления в верхней части этого класса и здесь же их определяем. Эти определения позволят нам выбрать кольцо и направление для каждого триггера. Посмотрите ниже на рис. 6.4, чтобы понять, как перечисление выглядит в инспекторе.
Рис. 6.4. Отображение общедоступного перечисления в инспекторе
Наш проект 133 Если бы вы выбрали любой из них, они бы отображали параметры, которые видны в приведенном выше коде. Еще одна маленькая деталь в коде – это первое значение в перечислении, которое мы присваиваем 0. Это произойдет по умолчанию; однако делать это явным вручную может быть хорошей привычкой. Когда кто-то смотрит на этот код, он точно знает, что значение перечисления начинается с 0. Кусочки головоломки Откройте файл FirstPuzzle.cs, который находится в папке сценариев и прикреплен к объекту FirstPuzzleGameObject в иерархии. Мы начинаем, как всегда, с определения переменных, которые хотим использовать. Для этого менеджера головоломок он должен иметь ссылку на каждое преобразование секций столбов, центральный столб, который отвечает за завершение головоломки, и свойства тайминга головоломки. Сразу после общедоступных переменных, которые мы будем назначать в инспекторе, у нас есть довольно много переменных, они не являются общедоступными, но назначаются и используются в логике класса. Уделите несколько минут, чтобы внимательно прочитать комментарии к ним. Мы будем ссылаться на эти переменные класса в оставшейся части данного раздела. Следующая часть определения объемнее, чем мы видели ранее. Мы вытащим всю инициализацию и пройдемся по каждой части. void Start() { // Кешируем ссылки на триггерные громкости и плеер triggers = GetComponentsInChildren(); playerController = FindObjectOfType(); // Случайные стартовые позиции outerPillars.eulerAngles = new Vector3(0f, Random.Range(-180f, 180f), 0f); middlePillars.eulerAngles = new Vector3(0f, Random.Range(-180f, 180f), 0f); innerPillars.eulerAngles = new Vector3(0f, Random.Range(-180f, 180f), 0f); // Начальное положение центрального шпиля centerSpire.position = new Vector3(centerSpire.position.x, centerSpireStartHeight, centerSpire.position.z); }
Что касается класса MonoBehaviour, от которого мы наследуем, мы используем метод Start для инициализации ссылок на кеш и начальных позиций столбов при запуске игры. Во-первых, нам нужно кешировать ссылки на каждый том триггера. Мы используем метод UnityEngine.Component, который доступен, потому что у нас есть директива usingUnityEngine; в верхней части этого файла. Это GetComponentsInChildren(); с универсальным типом. Вы можете поместить любой тип вместо FirstPuzzleTrigger в приведенном выше коде. Это может быть Image или Transform. В нашем случае мы хотим захватить каждый триггер. Поясним, почему они нужны нам таким образом, в ближайшее время. Просто знайте, что все они в ведерке под боком и ждут, когда их вызовут. Далее нужно использовать FindObjectOfType, который является еще одним методом UnityEngine, но он относится к классу Object. Это часть библиотеки Uni-
134 Взаимодействия и механика tyEngine, и мы уже запрашиваем доступ к ее методам. FindObjectOfType найдет контроллер персонажа и вернет его в переменную playerController. Следующие три строки предназначены для настройки вращения колец. Мы хотели, чтобы они были уникальными, поэтому если кто-то будет играть в игру более одного раза, то каждый раз они бы немного отличались. Наконец, у нас есть позиция устанавливаемой головоломки. Мы используем эту линию, чтобы установить высоту центрального шпиля. После того как загадка будет завершена, центральный шпиль поднимется, чтобы с ним можно было взаимодействовать. Далее можно будет перейти к следующему разделу. Мы хотели, чтобы завершение головоломки анимировалось, чтобы визуально обозначить доступ идти вперед. Теперь перейдем к методу Update, который тоже из MonoBehaviour. Самое интересное в этой головоломке – то, что есть много мест, где мы мало что делаем. В основном нам нужно просто подождать, пока персонаж не переместит столбы в нужное положение. То, как мы запускаем раздел обновлений, похоже на систему блокирования на водяном пути. Итак, вы должны закончить первый шаг, чтобы перейти к следующему шагу. У нас есть очень упрощенный логический поток для этой системы на рис. 6.5. Первая головоломка завершена?
Да
Отобразить победу?
Да
Положение пазла конечное?
Нет
Нет
Нет
Проверка на победу
Метод отображения победы
Поставить головоломку в конечное состояние
Да
Головоломка завершена
Рис. 6.5. Базовый поток блокировки для менеджера головоломок
Давайте держать в уме рис. 6.5 до конца работы с менеджером головоломок. Первым шагом здесь является проверка на победу. Давайте углубимся в этот блок процесса. Победа зависит от того, насколько точно все три столба выровнены с желаемыми значениями вращения. outerAligned = CheckAlignment(outerPillars, correctRotationOuter);
Мы проверяем каждый кадр на предмет правильного выравнивания. Поскольку мы проверяем выравнивание для трех отдельных элементов, нет нужды писать код для всех трех колец. Давайте напишем его один раз, а затем попросим ссылаться на метод для каждого компонента. Это называется рефакторинг. Копнув еще глубже, мы должны разобрать, как он проверяет выравнивание. bool CheckAlignment(Transform pillarGroup, float correctRotation) { return Mathf.Abs(pillarGroup.eulerAngles.y - correctRotation) < correctThreshold; }
Наш проект 135 Во-первых, нужно, чтобы он возвращал логическое значение. Это очень полезно, когда вы хотите использовать условное выражение для ответа. Мы запрашиваем текущий столб и правильное значение поворота. Смотрим на абсолютное значение текущего поворота в значении y минус правильный поворот. Мы берем это значение и проверяем, меньше ли оно порога, который допускаем для «близости». Если да, то externalAligned вернет true. Если все три столпа возвращают true, то CheckForVictory вернет true, что позволит перейти к следующей части в нашей блокировке. Следующий блок отображает победу. Это похоже на обычный блок, являющийся просто отображением для отладки; однако здесь есть небольшая логика, которая помогает нам с конечным блоком. victoryStartTime = Time.time; outerStartVictory = outerPillars.eulerAngles; middleStartVictory = middlePillars.eulerAngles; innerStartVictory = innerPillars.eulerAngles;
Эти четыре устанавливаемых значения очень важны. Поэтому, прежде чем перейти к следующему блоку, нам нужно установить значения. Потенциально мы могли бы сделать это в финальном блоке; однако иногда полезно настроить каждый бит логики в блокировке в начале, чтобы вы могли легко отлаживать и точно знать, где находитесь и какие данные нужны именно в этой точке логики. Чтобы закончить последний блок, мы знаем, что нужно записать текущую информацию о столбах. Мы сознательно сохраняем возможность того, где необходимо разместить столбы, поэтому такой вариант не означает, что решение головоломки всегда будет одним и тем же. Теперь, сохранив значения столбов и отобразив нашу победу в консоли для отладки, можем перейти к финальному блоку. Последний блок находится в методе PerformVictoryLerp. Не торопитесь, просматривая весь метод. Мы разберем один Lerp ниже. Интересно, что этот метод в основном просто анимирует некоторые элементы окружающей среды для завершительной части и позволяет нам закончить блок, поэтому больше не нужно проверять повороты в этой головоломке. outerPillars.eulerAngles = Vector3.Lerp(outerStartVictory, outerEndVictory, lerpVal);
Мы видели нечто подобное у персонажа с использованием метода Slerp. Метод лучше всего подходил для сферических нужд. Lerp (linear interpolation) – это линейная интерполяция. Вы преобразуете значение в другое значение того же типа в течение определенного периода времени. В данном случае это значения вращения столбов до заранее определенных значений вращения в случае победы, до которых нам нужно довести столбы, потому что есть небольшая свобода действий, которую мы даем каждой секции. Работа с этим методом может показаться сложной. Если вы почувствуете себя в тупике, просто посмотрите на каждую строку и внимательно проработайте их. У каждой строки есть задача, и она обеспечивает контекст для времени Lerp или сама переключается на другое значение за это время. В конце также есть метод PrintDebug вне нашей системы блокирования. Данный метод позволяет нам в любой момент проверять, что происходит в голо-
136 Взаимодействия и механика воломке. Потратьте некоторое время, чтобы изучить этот метод, и предположите, что он может вам показать, а затем запустите игру и проверьте, верны ли ваши предположения. Появилось ли в консоли что-то, чего вы не ожидали? Посмотрите, сможете ли вы найти его в коде, следуя логике игры и сопоставляя с моментом, когда вы увидели сообщение консоли. Следующие вопросы, которые могут прийти на ум: «Это отличный способ, но как мы на самом деле его контролируем? Кроме того, почему мы не рассмотрели метод RotatePillar?» Это отличные вопросы! Давайте обсудим их в следующем разделе. Управление головоломкой Должны ли мы поместить регулятор громкости триггера головоломки? Мы считаем, что все механизмы управления должны находиться на объекте, содержащем элемент управления. Мы сделали еще один скрипт под названием FirstPuzzleControl.cs в папке скриптов, который будет прикреплен к игровым объектам персонажа. Этот скрипт отвечает за настройку цвета громкости триггера, а также за вызов поворота из класса FirstPuzzle. Мы написали это таким образом, чтобы менеджер головоломки следил за вращением каждого кольца. Даже если персонаж является объектом, инициирующим метод RotatePillar с входными данными, менеджер головоломок должен вращать любую секцию столба, с которой взаимодействует игрок, поскольку он владеет этими объектами GameObject. Думать об этом таким образом немного нестандартно. Попробуйте представить менеджера, владеющего игровыми объектами и говорящего им, что делать. Объекты уже упоминаются в нашем скрипте, и мы должны сохранить их. Другим вариантом было бы также сослаться на них в скрипте управления, который находится на персонаже, и тогда у вас получится несколько ссылок, и это потенциально может вызвать ошибку, которую вы можете не заметить. Попытайтесь централизовать свои игровые объекты в одном скрипте, управляющем ими в максимально возможной степени. Метод RotatePillar довольно сложен. В нем нам нужно не только вращать кольца, но и толкать персонажа вместе с ним. Как мы это делаем? Давайте посмотрим. public void RotatePillar(FirstPuzzleTrigger trigger) { // вращать столбы float rot = (trigger.triggerDirection == FirstPuzzleTriggerDirection. Clockwise ? pushSpeed : -pushSpeed) * Time.deltaTime; trigger.transform.parent.parent.Rotate(Vector3.up, rot); // Удерживать игрока запертым в громкости триггера лицом к столбу. Нам нужно отключить CharacterController сейчас // при установке новой позиции, иначе новая позиция будет перезаписана текущей позицией игрока playerController.enabled = false; float origY = playerController.transform.position.y; playerController.transform.position = new Vector3(trigger.transform. position.x, origY, trigger.transform.position.z); playerController.transform.forward = trigger.transform.forward; playerController.enabled = true; }
Наш проект 137 Сначала нужно знать, насколько далеко мы собираемся повернуть игровые объекты столба. Мы хотим присвоить угол поворота переменной в области действия метода. Будем использовать условную операцию, чтобы определить направление вращения, по часовой стрелке или против, и умножим на deltatime, чтобы иметь дело с изменениями частоты кадров. Затем станем вращать элемент, используя parent.parent.Rotate, вдоль его вектора up. Угол поворота и направление определяются строкой выше (rot). Одна из проблем заключается в том, что персонажу нужно двигаться вместе со столбом, с которым он взаимодействует. Вторая проблема – в том, что столб вращается, поэтому нам нужно повернуть персонажа лицом к столбу, который он толкает. Чтобы сделать это, отключим возможность игрока двигаться, а затем напрямую манипулируем персонажем до местоположения триггера, затем остановим его там, пока удерживается кнопка взаимодействия. Мы также направим персонажа на столб, используя forward vector громкости триггера. И наконец, передадим управление обратно игроку. Это позволяет сделать так, чтобы персонаж не застрял в бесконечном цикле толкания столба. Вот оно! Мы только что сделали нашу первую головоломку. Что происходит дальше после решения этой головоломки, так это то, что дверь пытается открыться, но немного ломается, и остается только узкое пространство для прохода. Давайте разберемся, почему это может быть полезно.
Ограниченные пространства Бывают случаи, когда игре нужно загрузить следующую сцену, но вы не хотите, чтобы загрузочный экран выводил игрока из погружения, или просто хотите добавить немного напряжения в окружающую среду. Возможно, вы захотите добиться и того, и другого! Ограниченное пространство является распространенным инструментом для достижения любой из этих ситуаций. Давайте рассмотрим, как мы используем такое пространство в нашей работе.
Проектирование Концепция узких пространств представляет собой интересное дизайнерское решение. Мы используем его двумя способами. Первый способ – добавить немного напряжения в исследование и движение. Мивари должна пройти через очень узкое пространство, которое, как она только что увидела, появилось в результате обрушения. Второй момент заключается в том, что это общий дизайн, используемый для перехода между сценами. Поскольку мы используем эту концепцию дизайна только на небольшом вертикальном срезе игры и нам не нужно загружать несколько частей карты, концепция нам не нужна, но это хороший урок для вас, начинающего дизайнера. Это помогает настроить ожидания для игрока, давая ему понять, что будут медленные участки с плотной анимацией и камерой, расположенной близко к игроку, чтобы увеличить интенсивность. Пока происходит такая более длительная анимация и движение, это даст системе время загрузить в память следующую область без загрузочного экрана. Данный трюк великолепен, поскольку он не мешает погружению, позволяя сохранить детали. Ничто так не разрушает недоверие, как наблюдение за тем, как перед вами загружаются объекты. После
138 Взаимодействия и механика того как вы закончите пробираться через закрытое пространство, камни естественным образом упадут и закроют проход. Это не только запрещает любое движение назад, но и дает ощущение необходимости двигаться вперед.
Реализация Первоначальная реализация проста. Мы заставим камеру Cinemachine перемещаться по пространству, чтобы получить представление о времени, необходимом для синематика, не позволяя игроку что-либо делать. Запустим реализацию через код следующим образом: Void SetPlayerEnabled(bool enabled) { var cams = playerRoot. GetComponentsInChildren(true); foreach(var cam in cams) { cam.gameObject.SetActive(enable); } playerRoot.GetComponentInChildren(). enabled = enable; }
Нам нужно найти виртуальные камеры у дочерних объектов и включить их, а также отключить персонажа игрока. В главе 12 мы будем вызывать данный код при запуске синематика. Но вместо виртуальных камер, которые есть в дочерних объектах, мы будем вызывать камеру, созданную для синематика. Эта реализация очень хорошо работает для настройки логики, и вы не беспокоитесь о нюансах каждого ролика, который требует очень много времени и анимации.
Области взаимодействия Это швейцарский армейский нож в механике. Существует так много способов использования взаимодействий, что мы не можем охватить их все в книге. Также нет особого смысла, поскольку определение этого лишает творчества, которое можно было бы попрактиковать. Это не тот инструмент, который следует детализировать с высокой степенью. Вместо этого давайте рассмотрим, как мы будем его использовать, а также некоторые мысли по этому поводу в целом.
Проектирование Поскольку у нас приключенческая игра-головоломка, будут точки, в которых нам нужны области, где, когда персонаж входит в них, что-то происходит. Это определение намеренно широкое. Мы также используем Cinemachine для основной камеры нашего персонажа, что позволяет подключать виртуальные камеры в определенных местах, когда вы запускаете свои области взаимодействия. Вот несколько примеров того, что мы можем делать с такими областями: перемещать камеры над обрывом, чтобы создать ощущение повышенной тревоги, спровоцировать падение камней.
Наш проект 139 изменять анимации ходьбы, чтобы она была медленнее, когда вы идете по воде, изменять освещение в окружающей среде, спавнивать игровые объекты. Этот список ни в коем случае не является исчерпывающим, поскольку области взаимодействия – это творческий инструмент интерактива. Мы используем их всего несколькими способами, но возможности безграничны. Дайте волю своему воображению, создавая области взаимодействий. Это очень важно для многих игр, особенно для нас, с нашей ориентированной на окружающую среду и на исследования механикой. Наши взаимодействия требуют, чтобы окружение объясняло игроку, что происходит и как двигаться дальше. В разделе скриптов ниже мы рассмотрим каждую область взаимодействия этой главы. В будущих главах их будет использоваться больше, особенно на этапе последних штрихов, поскольку так мы улучшим опыт с помощью множества небольших взаимодействий. Это поможет сделать окружающую среду и игровой процесс более захватывающими.
Реализация К счастью для нас, вы видели лишь две версии. Просмотрите реализации того, что мы рассмотрели ранее, и обратите пристальное внимание на области. Они уникальны в своем использовании и могут преподать вам несколько ценных уроков о разработке в среде, где не все художественные ассеты завершены. Возможно, было бы неплохо подумать и о других способах использования областей взаимодействия. Были ли какие-то области, которые вы, возможно, хотели добавить? Почему бы нам не сделать небольшой обзор того, где мы используем их в нашей игре? Мы применяем интерактивные тома везде, где можно использовать действие ввода. Примером этого могут быть кнопки лестницы для доступа к ней. Мы добавили область, благодаря которой знаем, что игрок находится в зоне первой головоломки. Это позволяет камере перемещаться в более выгодное положение для визуального понимания головоломки. Триггеры на деталях головоломки сообщают вам, что вы находитесь достаточно близко, чтобы взаимодействовать с ними. Существует область, сообщающая что персонаж вошел в триггерную точку для входа в ограниченное пространство. На пересечении моста есть небольшая область для изменения ракурсов камеры для более кинематографического вида. В конце моста есть область, чтобы вызвать другой синематик в ограниченном пространстве. На уступе есть триггер, который сбрасывает на вас валун, после чего вы поднимаете руку для защиты. Это заставит вас открыть для себя новую силу, соответственно, и новую механику. Больше триггеров используется, чтобы открыть другую дверь. На предметах, с которыми вы можете взаимодействовать с помощью своих телекинетических способностей, есть триггеры. На последних деталях головоломки так же есть триггеры.
140 Взаимодействия и механика Это сводка всех триггеров, которые являются частью нашего основного игрового процесса. Есть несколько других, которые имеют дело с окружающей флорой и фауной, но они являются простыми триггерами столкновения, ответственными за небольшие изменения или простое движение птиц или оленей. Они находятся в случайных местах в косметических целях.
Заключение В шестой главе мы рассмотрели дизайн и реализацию взаимодействий и механики. Несмотря на то что опыт и взаимодействие игрока казались довольно простыми, глубина дизайна возможностей позволяла игроку знать свои пределы и ориентироваться в игровом процессе. Мы уделили много времени на разговоры о взаимодействиях и механике. Определили игровые циклы и разобрали наборы инструментов механики. Это был очень быстрый и короткий урок по различным игровым процессам. Наконец, мы кое-что обрушили в игре. Мы разобрали взаимодействие на лестнице и то, как им можно управлять. Также рассмотрели, почему существует проблема с лестницей и где должны быть решения. Затем перешли к дизайну первой головоломки. После полного объяснения разобрали нашу версию реализации. Как только эта головоломка завершена, за ней следует узкий космический сегмент, который можно было бы использовать для загрузки остальной части уровня, если бы мы работали над более масштабным проектом. Наконец, был небольшой раздел о том, как использовать области взаимодействия. Поскольку в предыдущей реализации мы использовали два разных типа областей взаимодействия, мы также рассмотрели их. В целом данная глава была очень насыщена информацией. Дайте себе немного времени, чтобы остановиться здесь и переварить то, что вы только что узнали. Даже если вы чувствуете, что можете двигаться дальше, давайте просто расслабимся и позволим мозгу все обработать. В следующей главе мы рассмотрим физическую механику и взаимодействия.
7 Взаимодействие RigidBodies и физики Во многих игровых взаимодействиях должна быть физика. Если у вас есть предметы, которые падают, подпрыгивают или просто реагируют на столкновение процедурным образом, вам, скорее всего, потребуется использовать компоненты Rigidbody на ваших игровых объектах. Этот компонент работает с физикой. Сначала мы рассмотрим несколько вариантов использования компонента Rigidbody. Далее нам понадобится некоторое время, чтобы объяснить, как мы используем физику во взаимодействиях для нашего проекта. Наконец, максимально подробно покажем скрипты, которые используются для достижения этих взаимодействий. Как всегда, файлы проекта на GitHub будут следовать структуре файла Readme. Темы главы включают: компонент Rigidbody, выявление столкновений, дизайн и реализацию, телекинез и физику.
Компонент Rigidbody Этот мощный компонент, ориентированный на физику, можно добавить в GameObjects, чтобы определить его положение посредством физики. По умолчанию простое добавление этого компонента к игровому объекту поставит его движение под влияние силы тяжести. Чтобы понять, как Unity использует физику, давайте рассмотрим компонент. На рис. 7.1 скриншот Rigidbody в Unity. Также существует компонент Rigidbody 2D, его не следует использовать для работы с 3D. Основная проблема заключается в том, что 2D- и 3D-версии физики не взаимодействуют друг с другом. Лучше выбрать один и придерживаться его! Мы пройдемся по всем частям компонента Rigidbody после рисунка.
142 Взаимодействие RigidBodies и физики
Рис. 7.1. Компонент Rigidbody
Mass Свойство Mass в Rigidbody относится к взаимосвязи этого объекта с массами других объектов. Это не заставит гравитацию влиять на него по-другому, но повлияет на столкновения с другими объектами. Например, если столкнутся два одинаковых игровых объекта, за исключением их массы на Rigidbody, предмет с большей массой будет действовать так, как если бы он был тяжелее. Как и в реальном мире, масса не заставляет предметы падать быстрее. Это происходит из-за сопротивления объектов.
Drag Объекты с Drag (сопротивление) уменьшат скорость, с которой они ускоряются из-за гравитации. Пример тому – парашют. Этот объект резко снижает ускорение падения. Например, у парашютиста очень низкое сопротивление, а когда он раскрывает парашют, сопротивление сильно возрастает. Это не зависит от вращения объекта.
Angular Drag Angular Drag (угловое сопротивление вращению) – это та же концепция, что и drag; однако он специально ориентирован на значения вращения. Если у вас очень маленькое значение углового сопротивления, объект будет вращаться при ударе или столкновении в зависимости от встречного угла сталкивающегося объекта. Если вы поднимете значение, он будет вращаться меньше.
Логическое значение Use Gravity Логическое значение Use Gravity просто позволяет гравитации воздействовать на GameObject, у которого Rigidbody является компонентом. Как показано на рис. 7.2, в меню Edit > Project Settings > Physics гравитация определяется как –9.81, что совпадает с гравитацией Земли. Настройка гравитации по оси Y на –9.81 будет наиболее привычной для игроков при эмуляции подобия земной
Компонент Rigidbody 143 гравитации. Если вы работаете с игрой с меньшей гравитацией и она все время одна и та же, можете установить ее в этом меню. Также можете сделать это в коде: Physics.Gravity = Vector3(0, 0, 0,);
Нули должны быть заменены значениями гравитации, которые необходимы, в основном в направлении Y.
Рис. 7.2. Project Settings – Physics settings
Логическое значение Is Kinematic При разработке уровня могут быть движущиеся элементы, которые должны влиять на физику другого Rigidbody во время выполнения. Очень простой пример, который вы можете себе представить, – это сфера с Rigidbody над большим кубом. Когда вы нажмете кнопку воспроизведения, сфера упадет и ударится о куб, как и ожидалось. Если бы вы установили логическое значение Is Kinematic в false и попытались повернуть куб, сфера осталась бы на месте и прошла сквозь куб. Это связано с тем, что куб не обновляется как движущееся тело после того, как сфера ударила его и остановилась. Включение данного значения полезно во время прохода оптимизации, и его можно установить для каждого известного статического элемента, который все еще должен иметь компонент Rigidbody. Тем не менее, если вам нужно обновить физику во время выполнения, установите для земли кинематический режим, и при вращении сфера будет реагировать, как и ожидалось, и попытается скатиться с наклонной нижней стороны куба. Обратите внимание, что это очень распространенная ошибка в начале работы с элементами физики. Если во время выполнения ваши элементы Rigidbody не двигаются так, как вы ожидаете, проверьте, должны ли они быть кинематическими.
Interpolate Interpolate означает возможность размещение объекта там, где его не было. В нашем случае нужно знать, пытается ли интерполяция достичь одного из трех параметров в нашем обновлении физики. Эти параметры: None: не интерполировать и не экстраполировать, Interpolate: поместить объект между текущим кадром и следующим кадром,
144 Взаимодействие RigidBodies и физики Extrapolate: предположить следующее местоположение из предыдущих кадров и поместить объект туда, куда, по вашему мнению, он мог бы поместиться. Выяснить подходящий параметр для интерполяции может быть сложно. Причина в том, что существует несколько вариантов интерполяции, что делает решение не таким простым. Есть несколько переменных для учета. Эти переменные могут содержать такие вопросы, как: «Как движется камера?», «Объект движется быстро?», «Вы беспокоитесь о том, что столкновение выглядит правильно?», «Вы беспокоитесь о том, что объект движется неправильно каждый раз, когда камера следует за его движением?» Простой ответ: если ваша камера следует за персонажем с Rigidbody, установите для него Interpolate, а для всего остального – None. Если немного погрузиться в систему физики, эта система вычисляется с фиксированным интервалом в отличие от рендеринга графики. Графика в игре может немного отставать и вставать на место, где физика всегда будет рассчитываться с фиксированным интервалом. Это может привести к появлению визуальных артефактов (ошибок), таких как клиппирование в стену. Такое клиппирование или любые другие объекты в вашей сцене будут заметны, если, например, камера приближенно следует за быстро движущимся объектом и сталкивается со стеной или окружающими игровыми объектами. Первоначально объект будет проходить сквозь стену, пока не обновится физика, а затем он будет обновляться, как будто объект отскакивает от стены. В этом случае вам следует выбрать опцию Interpolate, потому что физическая система будет интерполировать промежуточные значения по мере рендеринга графики. Это не позволяет выполнять клиппирование во время движения с точки зрения физики, что снижает производительность, поскольку вычисляет значения в других интервалах, чем обычно. Exterpolate хорошо помогает выяснить, какими будут значения в будущем. Это полезно для симуляции летающего объекта, но не подходит для обнаружения столкновений, поскольку предполагается, что он пролетел мимо стены или объекта, и происходит клиппинг с более высокой частотой кадров и движением. При внимательном отслеживании движения можно использовать Interpolate или Extrapolate. Лучше всего начать с Interpolate и посмотреть, подходит ли он для движения с точки зрения вашего опыта. Если он кажется медленным, попробуйте Extrapolate. Взвесьте все за и против с более высокой скоростью движения в ваших последовательностях действий, чтобы определить, какой метод интерполяции подходит. Понимание всего этого позволит вам выбрать наилучший вариант физических значений и графического представления элементов, которые вы симулируете с помощью физики.
Обнаружение столкновений При использовании физики для определения положения GameObject должны быть проверки столкновений, чтобы определить, столкнулся ли ваш объект с другим объектом, независимо от того, остается ли он неподвижным или движется в сцене. Это интересная дилемма теперь, когда вы узнали, что физика
Компонент Rigidbody 145 фиксирована, а рендеринг не фиксирован. Физическая система не может предположить, что каждый объект использует для типов столкновений или интерполяции. Нам нужно иметь несколько вариантов, которые наилучшим образом соответствуют физическим потребностям каждого игрового объекта внутри опыта. Существует четыре различных типа обнаружения столкновений: дискретный (Discrete), непрерывный (Continuous), непрерывный динамический (Continuous Dynamic) и непрерывный спекулятивный (Continuous Speculative). Если у вас есть GameObject, который движется быстро, он может пройти сквозь другой GameObject, а это означает, что он не будет знать, что столкнулся с коллайдером, и станет продолжать двигаться через него по мере обновления физики. Этого можно избежать с помощью режимов обнаружения столкновений. Каждый режим по-разному влияет на производительность; однако общее правило заключается в том, что для быстро движущихся объектов устанавливается значение Continuous Dynamic, а для объектов, с которыми они могут столкнуться, должно быть установлено значение Dynamic. Другие варианты объясняются далее.
Discrete Этот режим обнаружения столкновений является лучшим для производительности, и название Discrete очень удачно, поскольку он проверяет физическое столкновение только через фиксированные интервалы, как упоминалось ранее. Если у вас есть стена с box-коллайдером и шар, достаточно быстро движущийся, чтобы его известное местоположение до стены не сталкивалось с ним, а следующее фиксированное обновление прошло мимо стены, значит, коллизии нет! Поначалу вы можете разочароваться, так как покажется, что это не работает, или, что еще более разочарует, это происходит только периодически, поскольку мяч мог столкнуться со стеной, когда вы запускали симуляцию пару раз. Следует понимать, почему это происходит, чтобы вы могли выбирать различные режимы в соответствии с потребностями физической симуляции. Причина в том, что обновление физики не понимает, что на объект должно было что-то воздействовать. Физический цикл в дискретном режиме будет проверять, нужно ли объекту изменить траекторию, только когда он находится в цикле. Если у вас есть быстро движущийся объект, определяемый как объект, перемещающийся на расстояние, превышающее его высоту или ширину за кадр, то может быть точка, в которой этот объект проходит мимо другого объекта, и физика не будет знать, как реагировать на это. Если нет быстро движущихся объектов, Discrete – отличный выбор. Если вы планируете иметь быстро движущиеся объекты, тогда ответом будет Continuous, но, пожалуйста, прочитайте об остальных параметрах, поскольку не все они взаимодействуют друг с другом интуитивно.
Continuous Если вы выберете Continuous, вы можете увидеть, что объект по-прежнему пересекает игровые объекты, которых вы, возможно, не ожидали. Очень важно понимать, что непрерывное обнаружение столкновений проверяет только, сталкивается ли ваш игровой объект со статическими объектами. Этот режим требователен к ресурсам, и его следует использовать с осторожностью.
146 Взаимодействие RigidBodies и физики Статические объекты – это GameObjects, которые находятся в сцене с компонентами коллайдера, но без компонента Rigidbody. Они не обновляются с физикой. После описания обнаружения столкновений будут режимы, работающие только со статическими игровыми объектами. Пример объекта, который будет использовать непрерывный режим, – быстро движущиеся игровые объекты, которым необходимо сталкиваться только со статическими элементами. Самый простой пример – Pachinko. Это игра, в которой маленький металлический шарик падает сверху вниз, ударяясь о статические предметы, и отскакивает от них. Все элементы на поле статичны, поэтому клиппинга не будет.
Continuous Dynamic Этот режим очень похож на Continuous; однако он также работает с игровыми объектами, в которых используется компонент Rigidbody. Данный режим стандартно используется в игровой механике. Как вы понимаете, добавление возможности работы с компонентом Rigidbody увеличивает стоимость ресурсов внутри игры. Более затратно, чем стандартный непрерывный режим. Примером непрерывной динамики может служить игра Smash Hit, в которую вы могли играть. Это мобильная игра, где вы играете на рельсах и двигаетесь вперед. Когда вы нажимаете на экран, металлический шар вылетает в сторону места, куда вы нажали. При столкновении стекло разбивается. Оно динамично и взаимодействует там, куда попадает мяч. Осколки также динамичны и взаимодействуют с окружающей средой при падении. Если бы стекло не было динамичным, мяч бы пролетел сквозь него. Из-за этого игра была бы менее увлекательной!
Continuous Speculative Слово «спекулятивный» предполагает в своем роде предположение. То есть система предполагает, произойдет ли столкновение. Данный режим делает то же, что и Continuous Dynamic, и объекты с этой настройкой могут сталкиваться как со статическими, так и с динамическими объектами GameObject; в то же время это менее затратно. Однако есть небольшая цена за точность. Два объекта, которые летят навстречу друг другу, могут в конечном итоге отскочить друг от друга, даже не коснувшись, если у них обоих установлен режим Continuous Speculative. Такое произошло бы потому, что оба объекта предполагают, где они будут в следующем кадре, что заставляет их думать, что они должны были отскочить друг от друга. Примером является игра под названием Beat Saber. Это игра, в которой вы находитесь в виртуальной реальности и должны ударять по блокам под определенным углом, чтобы правильно их активировать. Если для обнаружения вашей сабли установлено значение Continuous Speculative, вы будете знать, что попадете в блоки, которые движутся к вам с высокой скоростью. Понимание всех режимов обнаружения столкновений поможет вам создать правильную настройку для работы, основанной на физике. Потратьте время,
Компонент Rigidbody 147 чтобы поиграться со всеми режимами в своем собственном проекте и понять, как они все работают вместе.
Ограничения Теперь, когда мы обсудили некоторые сложные вопросы, давайте вернемся к более простой теме: ограничениям (constraints)! Они делают именно то, что вы подумали. Если ваш элемент не должен двигаться или вращаться вокруг определенной оси, вы можете ограничить его. Примером является платформерная игра с движущимися предметами. Представим, что вы хотите, чтобы они двигались, но, возможно, только по определенной оси. Чтобы гарантировать, что предмет не сдвинется с курса, вы можете ограничить GameObject в направлении X, Y или Z, чтобы он никогда не обновлялся в эти направления. Это было последнее редактируемое поле компонента Rigidbody. Последний раздел посвящен полям только для чтения для отладки во время выполнения. Давайте посмотрим, какую информацию вы можете получить из этих полей.
Info Блок Info компонента Rigidbody необходим для работы с физикой и отладки странного поведения, которое может возникнуть. Каждое приложение может иметь уникальные проблемы. Глядя на объект Info во время игры, вы можете легко отладить происходящее. Этот раздел имеет много значений. Speed: Величина скорости. Velocity: Скорость изменения положения Rigidbody. Angular Velocity: Вектор угловой скорости Rigidbody, измеренный в радианах в секунду. Inertia Tensor: Диагональная матрица в системе отсчета, расположенной в центре масс этого тела и вращаемой Inertia Tensor Rotation. Inertia Tensor Rotation: Вращение тензора инерции. Local Center of Mass: Центр масс относительно начала преобразования. World Center of Mass: Центр масс Rigidbody в мировом пространстве. Sleep State: Стратегия оптимизации не всегда учитывает каждый объект с двумя настройками: Awake: Физика рассматривает это Rigidbody, Asleep: Физика не рассматривает это Rigidbody. Каждое из перечисленных значений имеет свое уникальное назначение в зависимости от того, что вы пытаетесь просмотреть или отладить во время выполнения. Работая с ранее упомянутым платформером, вы можете подумать, что ваша платформа должна быть выравнена относительно вашего персонажа, но что-то отодвинуло ее в сторону ровно настолько, чтобы не дать персонажу приземлиться на нее. В блоке Info вы можете наблюдать за движением или скоростью изменения положения. Если в направлении Z не должно быть скорости изменения положения, то просмотр этого значения позволит вам узнать, работает ли оно так, как задумано. У нас теперь есть четкое представление о том, как работает 3D-компонент Rigidbody, и мы можем обратиться к этим страницам, если при построении взаимодействий, ориентированных на физику, возникнут какие-то проблемы.
148 Взаимодействие RigidBodies и физики
Вопросы проектирования и реализации Очень легко попытаться добавить физику к каждому из ваших игровых объектов, чтобы получить движение в ваших взаимодействиях. Не каждому предмету требуется Rigidbody для завершения его движения таким образом, чтобы ваши взаимодействия обеспечивали фантастический опыт. В конце концов, все дело в кадрах в секунду (fps). Попробуйте сделать любой движущийся объект без компонентов Rigidbody, но если они нужны, то добавьте их.
Взаимодействие телекинеза и физики Для первой головоломки нашей игры мы сосредоточились на том, чтобы сделать повествование об окружающей среде ключевым моментом. С того момента, как вы войдете в первую комнату, взгляд будет направлен на заднюю дверь, в которой находится решение головоломки. В финальной головоломке нам нужно заставить игрока использовать больше умственных способностей для решения головоломки, а не для поиска ответов вокруг себя. Для этого мы решили дать игроку силу телекинеза, которую, как понимает Мивари, она имела внутри себя все это время. У нас есть три шага, чтобы довести игрока до этой точки понимания.
Падающие камни Телекинез еще не был замечен в игре ни в какой форме. Некоторая магия исходила от ее ожерелья, но нам нужно предоставить некоторую информацию, чтобы сообщить игроку, что в персонаже есть что-то магическое. Синематик хорошо подходит для этого. Нам нужно спроектировать взаимодействие.
Проектирование После решения головоломки с первой дверью вы попадаете в большой коридор со старыми статуями из вашего прошлого. Это хороший взгляд на культуру прошлого ее расы. Здесь нечего решать; вы просто прогуливаетесь. За последней статуей находится тесное пространство, через которое можно пройти к тропинке в скале. Примерно на полпути падают камни, что запускает синематик, в котором Мивари защищает себя от этих падающих камней с помощью своего телекинеза. Она выглядит растерянной, нужно двигаться вперед, чтобы узнать, что происходит. Ее авантюрная сторона личности манит ее двигаться дальше.
Реализация То, что нужно реализовать здесь, состоит из двух частей. Одна большая часть – это ролик о скалах и Мивари. Синематики – это когда пользователь не имеет власти над взаимодействием. Это полезно для получения знаний, но не следует злоупотреблять, потому что игра может стать похожей на интерактивный фильм, поэтому используйте сдержанно. Вторая часть – это камни, основанные на физике как вторичное движение от падения валуна. Синематик будет запущен так же, как и раньше: мы отключаем возможность игрока манипулировать Мивари и камерой, затем переходим к анимации си-
Взаимодействие телекинеза и физики 149 нематика, перемещая камеру и выделяя нужный объект, в данном случае валун. Если вам нужно освежить память, просмотрите главу 6 «Взаимодействия и механика» во время реализации ограниченных пространств. Однако камни, основанные на физике, нельзя просто анимировать. Нам нужно ощущение, как будто они упали сами по себе, соответственно, покажется то же самое с валуном, что помогает создать впечатление реалистичности. Несмотря на то что все это раскрывает способность телекинеза, исходящую от Мивари, нам нужно, чтобы игрок выполнял взаимодействие, иначе получится просто способность, которую он не может использовать. Далее рассмотрим взаимодействие с игроком.
Сломанный пьедестал Это первый раз, когда игрок может использовать вновь обретенную силу Мивари. Нам нужно спроектировать эту головоломку так, чтобы ее невозможно было пропустить, потому что игрок не привык использовать эту силу. Этот пьедестал – уменьшенная версия финальной головоломки. В упомянутой микроголоволомке вам нужно поместить упавшую деталь на пьедестал, чтобы починить его. Сейчас нужно быть очень осторожными с разработкой, чтобы с уверенностью сказать, что опыт игрока объясняет, как это работает, прежде чем он коснется кнопки взаимодействия. Давайте пробежимся по проектированию вместе.
Проектирование После того как мы пройдем по тропинке утеса и через небольшой мост, который разрушится, путь назад станет непроходимым. Единственный способ пройти дальше – через большую дверь. Когда мы подойдем к ней, она начнет открываться в огромную просторную пещеру с водой, скопившейся на дне, с руинами на заднем плане и с обрывом на верную гибель. Прямо перед Мивари находится сломанный пьедестал, но обломок лежит на земле рядом с ним. Глядя на него, мы видим, что он обрамлен тем же цветом, что и сила, защищавшая Мивари от падающих камней. В главе 8 мы отобразим помощник пользовательского интерфейса, показывающий, какую кнопку нажимать. Это позволит взаимодействовать с ее способностью, привязанной к кнопке, чтобы предоставить игроку свободу действий. Когда мы нажимаем кнопку, Мивари поднимает отломанный кусок с земли и устанавливает его на пьедестал, где он прикрепляется и загорается. Нажатие кнопки взаимодействия затем превратит открытое пространство в ночную сцену, и вода поднимется снизу, открывая путь к руинам.
Реализация Мы знаем, что механика, которую мы хотим включить сюда, является подмножеством финальной головоломки. Поэтому не хочется писать код только для единственного элемента, так что вместо этого мы настроили его как простой автономный, используя public enum. Чтобы было как можно легче читать, мы попросим вас не торопиться с данным разделом, посвященным последней головоломке. Мы объясним некоторые более продвинутые функции, на чем все строится далее. В данном коде
150 Взаимодействие RigidBodies и физики мы используем кое-какие удивительные тайминги Unity, которые потребуют некоторых пояснений. Итак, давайте перейдем к окончательному проектированию головоломки, а затем разберем все части этой и реализации последней головоломки.
Последняя головоломка Мы добрались до большой финальной головоломки. К счастью, нашлось время, чтобы показать игроку, что Мивари получила от стресса из-за падающего на нее валуна. Затем мы научились использовать способность, чтобы добраться до головоломки, починив сломанный пьедестал. Теперь у нас эта головоломка не совсем понятна, но позволяет окружающей среде научить игрока тому, что ему нужно делать. Давайте погрузимся в детали, чтобы разобраться, как мы создаем последнюю головоломку.
Проектирование Теперь, когда вы добрались до руин, на заднем плане есть какая-то архитектура, где подсвечиваются руны на колоннах. Это соответствует некоторым ветвям на земле, которые соединяются со всеми колоннами. Головоломка состоит из шести колонн, соединяющих питание с главным деревом в центре руин, к которому идут ветви. Ветви подключены правильно только от трех колонн. Мивари нужно использовать свой телекинез, чтобы правильно соединить колонны с ветвями на земле. Воздействие силы к дереву открывает небольшой отсек в нем, в котором находится тиара. В синематике тиара раскрывается и завершает игровой процесс нашего вертикального среза. Теперь, когда у нас есть общее представление о том, что нужно сделать, давайте перейдем к реализации.
Реализация Реализация этой головоломки является завершением механики телекинеза. Когда мы писали следующую часть, мы позволили себе перейти к более сложным темам. Чтобы убедиться, что это имеет смысл, рассмотрим здесь все темы и максимально разберем их. Обязательно обращайте внимание на мелочи, так как здесь есть фрагменты информации, которые поначалу кажутся скрытыми или нелогичными. Темы программирования, которые мы рассмотрим: порядок выполнения, статические методы, UnityAction (делегат), сопрограммы. Давайте сначала рассмотрим порядок выполнения для Unity. Мы не тратили время на разговоры о том, как это работает с точки зрения внутреннего устройства. Порядок выполнения Существует порядок выполнения каждого кадра во время выполнения кода или воспроизведения внутри редактора и открытия сборки. Мы бы показали
Взаимодействие телекинеза и физики 151 вам скриншот блок-схемы, но он слишком большой. Вместо этого мы поместим здесь ссылку, а также поисковый запрос, чтобы вы могли выполнить поиск в интернете и нашли веб-сайт, где увидите эту блок-схему. Здесь я расскажу о темах более высокого уровня и о том, почему они важны, в каждой части, на которую они влияют. https://docs.unity3d.com/Manual/ExecutionOrder.html. Поисковый запрос: Unity Execution Order. Основная концепция здесь заключается в том, что должна существовать иерархия выполнения определенных битов информации. Нам нужно было хорошенько подумать об этом, чтобы иметь представление о том, что будет происходить в каждом кадре. Неудобная правда в том, что действительно есть над чем подумать. Вот список в хронологической форме с терминологией высшего уровня для порядка выполнения и небольшим фрагментом информации по каждому из них. Инициализация: только для Awake и onEnable. Редактор: сброс при добавлении скриптов и не в режиме воспроизведения. Инициализация: вторая часть инициализации предназначена для класса Monobehaviour метода Start. Физика: здесь будут происходить все обновления физики. Потенциально его можно запускать более одного раза за кадр, если фиксированный временной шаг установлен выше, чем время обновления кадра. События ввода: любой ввод, не связанный с обновлением, например OnMouseDown. Игровая логика: обновление, логика и выдача сопрограмм, события анимации, запись свойств и запуск LateUpdate. Это станет более очевидным при реализации игровой логики далее в этой главе. Рендеринг сцены: многие функции рендеринга сцены запускаются в каждом кадре для обработки объектов с камеры, видимых объектов и пост-рендеринга. Мы не будем подробно разбирать это; если вам интересно, пожалуйста, прочитайте руководство по порядку выполнения для получения дополнительной информации. Рендеринг Gizmo: в частности, метод OnDrawGizmo редактора Unity. Рендеринг графического интерфейса: метод OnGui, который может выполняться несколько раз за кадр. Конец кадра: позволяет приостанавливать или завершать сопрограммы в конце кадра, ожидая завершения всех остальных перед повторным запуском в верхней части раздела игровой логики. Приостановка: когда приложение было приостановлено; прежде чем приложение будет приостановлено, запускается один кадр. Вывод из эксплуатации: очищает память с помощью OnApplicationQuit, OnDisable и OnDestroy в указанном порядке. Прежде чем мы перейдем к следующему разделу, вам нужно кое-что понять. Именно сейчас вам ни в коем случае нет нужды понимать весь список. Там есть чему поучиться, и если вы перейдете к документации по порядку выполнения,
152 Взаимодействие RigidBodies и физики то увидите каждый из перечисленных методов более подробно. Мы покажем части выполнения и объясним, что влияет на код в оставшейся части этой главы. Ключевым выводом из хронологического списка перечисленных разделов более высокого уровня является то, что у Unity есть порядок. Это облегчающая концепция, чтобы обернуть вашу голову как разработчика. Когда вы не понимаете, почему что-то происходит именно так, а не иначе, вы можете положиться на это, благодаря чему увидите, не является ли что-то проблемой порядка выполнения, с которой можете столкнуться. В следующих разделах у нас будут изображения раздела порядка выполнения, на который необходимо обратить внимание. Это позволит вам увидеть, как его можно использовать для вашей будущей работы разработчика. Теперь, когда мы рассмотрели порядок выполнения, следует перейти к коду. Чтобы заставить работать механику телекинеза, мы используем три скрипта: PhysicsPuzzleTrigger.cs PhysicsPuzzlePiece.cs FinalPuzzle.cs В PhysicsPuzzleTrigger.cs есть две части кода, о которых важно знать в первую очередь: класс PhysicsPuzzleTrigger и enum PhysicsPuzzlePieceType. Сначала мы займемся PhysicsPuzzlePieceType, так как в него гораздо проще попасть, чем в триггер. У нас есть enum, котороый позволяет нам выбрать, какой тип кусочка головоломки находится в GameObject. Мы определяем это следующим образом: public enum PhysicsPuzzlePieceType { First = 0, Second, Third, Intro, Any }
Затем реализуем в скрипте PhysicsPuzzlePiece.cs следующим образом: public class PhysicsPuzzlePiece : MonoBehaviour { public PhysicsPuzzlePieceType pieceType; }
Добавляя скрипт PhysicsPuzzlePiece.cs к любому игровому объекту, мы получаем раскрывающийся список для выбора его типа. Это очень полезно, когда вы хотите, чтобы явные элементы соответствовали друг другу. Мы делаем это, чтобы использовать ту же механику, но разрешить для различных типов головоломок. Мы сказали в разделе «Сломанный пьедестал» выше, что объясним это в рамках реализации всей механики. Что мы делаем, так это позволяем параметру Intro соответствовать этой механике и быть явным для этого действия. Несмотря на то что невозможно получить части финальной головоломки в этом месте, это отличная практика для обеспечения соответствия данных вашему коду.
Взаимодействие телекинеза и физики 153 Вернемся к коду PhysicsPuzzleTrigger.cs. Мы начинаем с объявления полей, с которыми мы привыкли работать до сих пор, но затем в строке 12 есть чтото уникальное, состоящее из двух концепций, которые нам нужно пройти. Это использование static и UnityAction: public static UnityAction OnPieceSlotted;
Предлагаю прервать описание того, что именно мы делаем с этой строкой, чтобы объяснить контекст того, что такое static и UnityAction. После этого мы перейдем к использованию их в нашем коде для этого механизма. Статические методы Статический метод, поле, свойство или событие можно вызывать в любом классе, находящемся в пространстве имен, без использования директивы using или наследования. Допустим, у вас есть один скрипт, в котором есть следующее поле: public class StaticTest { public static int StaticInt = 10; }
Затем может быть другой скрипт в том же проекте, который, не вызывая специально при его использовании или наследовании, мог бы получить к нему доступ следующим образом: public class UseStaticTest { int BaseNumber = 0; int NewNumber = BaseNumber + StaticTest.StaticInt; }
Само по себе это может показаться не очень полезным, но концепция – это важная часть, которую следует усвоить в настоящее время. К элементам класса Static могут обращаться другие классы, просто используя имя класса перед требуемым элементом. Примером того, как это обычно используется, является подсчет чего-либо, поскольку поле static имеет только один экземпляр. Мы используем его для хранения UnityAction. UnityActions UnityAction – это специфичный для Unity делегат. Делегат в C# – это своего рода общая концепция метода, имеющего список параметров, который также возвращает определенный тип. Интересно, что UnityAction по умолчанию возвращает void. Обычный способ объяснения делегатов – через концепцию модели подписки. Это означает, что делегат ищет методы, которые будут присоединены к нему, и когда что-то использует делегат, он попытается запустить прикрепленные методы, пока методы возвращают один и тот же тип. Звучит немного абстрактно, поэтому давайте рассмотрим пример. Мы будем использовать UnityAction MathAction, чтобы добавить количество нажатий кнопки, а затем посмотреть, является ли это новое число четным или нечетным:
154 Взаимодействие RigidBodies и физики using UnityEngine.UI; public class UnityActionTest : MonoBehaviour { public Button AddButton; private UnityAction MathAction; float TimesClicked; void Start() { AddButton = GetComponent(); MathAction += AddOne; MathAction += CheckEven; AddButton.onClick.AddListener(MathAction); } void AddOne() { TimesClicked++; Debug.Log("Clicked count : " + TimesClicked); } void CheckEven() { if (TimesClicked % 2 == 0) { Debug.Log("This click was even!"); } Else { Debug.Log("ThIs ClIcK WaS OdD."); } } }
Так как мы используем класс Button, обязательно импортируйте UnityEngine. UI, чтобы можно было использовать кнопки из этого класса. Следуя по строкам вниз, мы создали новый UnityAction с именем MathAction. При запуске мы захватили кнопку, чтобы добавить к ней логику. Затем присоединили к UnityAction методы AddOne и CheckEven. Значок +=, который вы видите, – это MathAction, присоединяющийся к этим методам по порядку. Оператор присваивания сложения – мы используем специальный «синтаксический сахар», чтобы сделать код более понятным для чтения и менее избыточным. Оператор присваивания сложения выглядит так: MathAction += AddOne
Другой вариант: MathAction = MathAction + AddOne;
Взаимодействие телекинеза и физики 155 Затем вы видите, что мы назначили UnityAction слушателю кнопки. При нажатии кнопки обе эти функции будут запущены, потому что UnityAction назначается им обеим. Прежде чем мы сможем углубиться в код, нам нужно рассмотреть еще одну тему – сопрограммы. Сопрограммы Сопрограмма позволяет распределить задачу по нескольким кадрам. Однако это не форма многопоточности. Каждое действие по-прежнему выполняется в основном потоке. Сила сопрограмм в том, что они позволяют направлять паузу через новый термин yield. Взглянув на порядок выполнения на рисунке ниже, вы, возможно, помните, что видели yield null после Update в разделе «Игровая логика». Если у вас не включена вкладка браузера с порядком выполнения, посмотрите на рис. 7.3. Небольшое примечание слева красиво сказано. Если сопрограмма ранее была в состоянии ожидания или приостановлена и должна возобновиться, она возобновится в той же точке порядка выполнения.
Если сопрограмма ранее завершалась, но теперь должна возобновиться, то выполнение происходит во время этой части обновления
Рис. 7.3. Игровая логика порядка выполнения
Потрясающе, не так ли? Вы спросите, откуда она знает, что нужно возобновить? Хороший вопрос, читатель. Она знает, что должна возобновиться из-за логики в коде. В Unity Docs есть фантастический пример базового перехода от непрозрачного к прозрачному с использованием сопрограммы. Давайте быстро пройдемся по нему: void Update() { if (Input.GetKeyDown("f")) { StartCoroutine(Fade()); } } IEnumerator Fade() { Color c = renderer.material.color; for (float alpha = 1f; alpha >= 0; alpha -= 0.1f) { c.a = alpha; renderer.material.color = c; yield return null; } }
156 Взаимодействие RigidBodies и физики Я выделил жирным шрифтом три момента, которые могут быть для вас новыми. StartCoroutine(Fade()) просит приложение запустить сопрограмму с помощью метода Fade. Вы запустите сопрограмму во время запуска игровой логики в нижней части операторов yield; для этого снова обратитесь к рис. 7.3. IEnumerator заявляет, что этот метод является итерируемым. Вспомните, когда вы в последний раз создавали метод. Ключевое слово перед именем – это тип. Мы используем void, если он ничего не возвращает, но, поскольку это будет итерироваться, он должен знать. Мы сообщаем об этом компьютеру, добавляя IEnumerable в качестве возвращаемого типа. Последняя часть – yieldreturn null. Сложновато при первом просмотре цикла for. В большинстве случаев return выведет вас из цикла, но, поскольку у нас есть yield, Unity спрашивает, все ли мы закончили в методе. Он приостанавливается после вычитания 0,1f из текущей альфы и ждет, пока часть игровой логики не запустится еще раз, чтобы повторить все это снова до тех пор, пока не будет удовлетворена логика цикла for. Как только это будет завершено, он больше не переходит в режим ожидания. Обобщая данный код, нажатие клавиши F приведет к исчезновению со сцены игрового объекта, на котором находится этот скрипт. Скорее всего, теперь вы достаточно хорошо разбираетесь в этих понятиях. Вернемся к коду в нашем проекте, чтобы завершить реализацию. Обратно в код Хорошо... мы немного отвлеклись, чтобы объяснить некоторые ключевые понятия, но сейчас вернемся обратно. Давайте откроем резервную копию PhysicsPuzzleTrigger.cs. Идея здесь заключается в том, что у персонажа есть телекинез, и, когда вы перемещаете предмет близко к его триггерной области, он затем сам переместится на место в течение переходного периода, который мы определяем. Мы видели OnTriggerEnter ранее, так что ничего нового с триггерной областью. Задача в том, чтобы предмет двигался сам по себе, поэтому нам нужно отключить кучу полей Rigidbody и коллайдер. Это делается в строках 28–33 в PhysicsPuzzleTrigger.cs. Далее здесь видим новый код. Нам нужно настроить ссылки на то, откуда и куда должны перемещаться элементы, так как этот скрипт находится на нескольких игровых объектах, поэтому укажем их относительное положение. Затем мы запускаем сопрограмму в строке 40. StartCoroutine(TransitionTween());
У нас есть код для изменения цвета триггера; это временно для отладки. Затем есть цикл tween, который является термином «между» для анимации и означает изменение движения в нашем случае. Наш цикл while работает до тех пор, пока установлено значение tweenDuration, нормализованное относительно того, сколько времени прошло с самого начала. Это определяется как delta. Затем делаем линейную интерполяцию положения и сферическую линейную интерполяцию вращения до преобразования, которым мы хотим, чтобы оно завершалось:
Взаимодействие телекинеза и физики 157 while (Time.time - tweenStart < tweenDuration) { float delta = (Time.time - tweenStart) / tweenDuration; tweenPiece.position = Vector3.Lerp(tweenStartPos, transform. position, delta); tweenPiece.eulerAngles = Vector3.Slerp(tweenStartRot, transform. eulerAngles, delta); yield return null; }
Наконец, мы видим, что yield return null! Теперь приостанавливаемся до следующего цикла игровой логики, если только tweenDuration не завершен и мы не входим в цикл while, что означает, что мы завершили tween. Положение и углы в строке 61 устанавливаем для движущейся части, чтобы убедиться, что преобразование готово для ссылки в нашем UnityAction. tweenPiece.position = transform.position; tweenPiece.eulerAngles = transform.eulerAngles; OnPieceSlotted?.Invoke(this,tweenPiece.GetComponent());
Теперь переходим к UnityAction: OnPieceSlotted?.Invoke(this, tweenPiece.GetComponent());
Выглядит интересно. Почему там знак вопроса? Существует условный оператор, называемый «оператором нулевого условия», который перед выполнением следующего метода спрашивает, является ли OnPieceSlotted нулевым или нет. Это еще один синтаксический сахар. Вы можете получить тот же результат, выполнив оператор if, проверяющий, является ли значение OnPieceSlotted нулевым. В случае с UnityAction это говорит о чем-то очень конкретном. Он спрашивает, было ли что-нибудь прикреплено к действию. Если для UnityAction назначен метод, то вызовите любую назначенную функцию со следующими аргументами: this GameObject и tweenPiece как тип PhysicsPuzzlePiece. Здесь происходит какое-то волшебство. Помните, мы назначили OnPieceSlotted статическим элементом класса PhysicsPuzzleTrigger? Что ж, откройте FinalPuzzle.cs, и давайте продемонстрируем мощь статических элементов. При запуске добавляем локальную функцию с именем OnPieceSlotted в статическое действие UnityAction из PhysicsPuzzleTrigger.OnPieceSlotted. Мы знаем, что, когда наш игрок помещает объект в правильное положение, к концу сопрограмме нужно обновить, какой это был объект. Это была финальная или первая головоломка? Мы определили это через наше перечисление в PuzzlePieceType: void OnPieceSlotted(PhysicsPuzzleTrigger trigger, PhysicsPuzzlePiece piece) { if (piece.pieceType == PhysicsPuzzlePieceType.Intro) { Debug.Log("FINAL PUZZLE INTRO SOLVED. Trigger environment transition here"); tempBridge.SetActive(true); }
158 Взаимодействие RigidBodies и физики else { numPiecesSlotted += 1;if (numPiecesSlotted >= 3) { Debug.Log("FINAL PUZZLE SOLVED! Trigger portal event"); } } }
Данный локальный метод, запускаемый из UnityAction, дает нам триггер, а piece сообщает, закончили ли мы первую головоломку или работали над финальной. Мы можем использовать любой сценарий позже в игре для этой конкретной механики, поскольку она статична и доступна для нас. Статика – это не только весело, когда трением носков по ковру можно шокировать ваших младших братьев и сестер. Это также магия в программировании! Мы только что программировали на Unity среднего уровня. Эти инструменты можно использовать во многих ситуациях, но их не всегда легко рассматривать как первый вариант решения вашей проблемы. Не торопитесь и проработайте каждый раздел. Сделайте несколько сопрограмм с GameObjects. Посмотрите, сможете ли вы создать свой собственный UnityAction в рамках одного скрипта, как мы показали выше. Протестируйте статические методы и посмотрите, как они работают, и со временем эти инструменты станут для вас естественными при разработке игр.
Заключение Какая насыщенная глава! Мы многое рассмотрели, поэтому я думаю, что здесь нужно небольшое резюмирование. Понятие физики изначально является сложной темой для решения. Мы используем его для симуляции в играх небольшого масштаба. Мы полностью рассмотрели компонент Rigidbody, а затем углубились в совершенно новую работу на C#. Для С# были пройдены: порядок выполнения, статические методы, UnityAction (делегат), сопрограммы. Все эти новые концепции являются инструментами для использования в вашем следующем проекте. Уделите столько времени, сколько необходимо, чтобы переварить их. В будущем вы увидите, что они используются почти в каждом проекте, с которым вы работаете. В следующей главе нам нужно добавить системы меню и пользовательский интерфейс, чтобы у пользователя было больше контекста для игрового процесса.
8 Пользовательский интерфейс и меню Набор визуальной информации и компонентов, размещенных на экране видеоигры, известен как пользовательский интерфейс (UI – User Interface). Интуитивно понятные пользовательский интерфейс и система меню дают вашим игрокам возможность получить качественный опыт. Такая интерактивность и прямое влияние на игровой результат игры называется player agency, проектирование которого имеет решающее значение для создания интуитивно понятного и успешного интерактивного опыта в вашем игровом мире. Также позволяет игрокам взаимодействовать с повествованием игры и с точностью – с игровым пространством. Пользовательские интерфейсы и системы меню в вашей игре также обеспечивают возможности для игроков. Возможности игрока – это общение с вашим игроком о том, как использовать объект в игре, передача элементов управления и навигация по игровому миру от начала до конца. Системы игрового меню дают игроку специальную возможность влиять на различные режимы игрового процесса. Данные игровые режимы сообщают игроку, когда начинать взаимодействовать, а также какие варианты и действия доступны до, во время и после игры. Вовлечение игрока в игру важно, но для опыта во время игры ваш интерфейс может иметь большее значение. Существует четыре формы пользовательского интерфейса: диегетический, недиегетический, пространственный и мета. Потратив некоторое время на разбор этих определений пользовательского интерфейса, вы лучше поймете, как мы будем использовать их в нашем проекте. Затем рассмотрим скрипты каждого из них, чтобы дать представление о правильном способе их реализации. В главе будут рассмотрены следующие темы: определение UI, элементы UI, UI в нашем проекте, система Unity Canvas, объекты UI в Unity. Начнем с объяснения пользовательского интерфейса.
160 Пользовательский интерфейс и меню
Пользовательский интерфейс Потребность в пользовательском интерфейсе – палка о двух концах. Вам нужно будет установить функции пользовательского интерфейса, чтобы опыт продвигался вперед, но это также может легко отвлечь игрока от этого опыта, если интерфейс не сделан правильно. Не всегда получается сразу создать механику, которая научит игроков взаимодействовать с миром, где они играют. Это может разрушить погружение, что не всегда совсем плохо, но должно быть понимание, как можно разорвать это погружение, не испортив опыт. Давайте поговорим о четырех формах пользовательского интерфейса, которые разбиты на два определенных пространства: повествовательное и внутреннее. Повествовательное поддается повествованию, основанному на пользовательском интерфейсе, тогда как внутреннее – это функциональный пользовательский интерфейс в самом игровом мире. Читая различные формы UI, помните, что это не исчерпывающие объяснения, и их нужно понимать больше как инструмент, помогающий разработать правильный UI для того опыта, который вы хотите предоставить. Рассматривая диегетические, недиегетические, пространственные и метаформы пользовательского интерфейса, мы объясним, как пользовательский интерфейс вписывается в простую сетку 2×2 внутренних и повествовательных функций. Сетка 2×2 ниже на рис. 8.1 является визуальным представлением того, как интегрировать целостное представление пользовательского интерфейса и включать его в общий игровой процесс. В следующих абзацах заголовок каждого раздела форм пользовательского интерфейса также будет дополнен двойным ответом «если это, то это». Нет
Диегетический
Мета
Да
Пространственный
Недиегетический
Нет
Повествовательное
Да
Внутреннее
Рис. 8.1. Дизайн пользовательского интерфейса
Пользовательский интерфейс 161 Ответы «Да» или «Нет» как на повествовательную, так и на внутреннюю функцию в сетке 2×2 выше помогают нам понять, какая форма пользовательского интерфейса необходима. Следите за тем, как мы подробно опишем каждую из этих четырех форм.
Диегетический – повествовательное «да», внутреннее «да» Пользовательский интерфейс, сочетающий внутреннее и внешнее пространство, называется диегетический. Данный тип интерфейса обязуется не нарушать погружение, предоставляя игроку информацию, необходимую для понимания внутреннего игрового пространства. Возможно, с помощью интерфейса вы захотите передать место, куда игроку нужно попасть, но при этом создать ощущение сложности. Для этого можно во время сюжета указать игроку направление и дать компас. Когда вы нажимаете кнопку, чтобы поднять компас, это дает игроку информацию без выхода из внутреннего пространства. Мы приведем в пример компас при обсуждении остальных четырех типов, чтобы посмотреть, сможем ли мы преобразовать его в другой тип. Теперь, когда мы объяснили диегетический тип пользовательского интерфейса, давайте взглянем на отличный пример из игры. При описании диегетического интерфейса на ум приходит жуткая игра под названием Dead Space. Студия Visceral Games компании Electronic Arts (EA) (распущенная и объединенная с EA Vancouver и EA Montreal; 17 октября 2017 г.) создала жуткую видеоигру о космическом ужасе на выживание, вдохновленную другими произведениями ужасов, такими как Resident Evil 4 и Silent Hill. Разработчикам игр из Visceral Games нужно было подумать о том, как игрок мог бы смотреть прямо в центр экрана и как можно больше сосредоточить свое внимание на нем. Таким образом, игрок мог одновременно наблюдать за мерзостями, скримерами, кровью и ужасами мира Dead Space и ориентироваться в повествовании Айзека Кларка (Isaac Clarke). Айзек – главный герой Dead Space и несчастный системный инженер космического корабля, который на протяжении многих лет попадает в различные неприятные ситуации. Как вы можете сделать это в ролевой игре, где у вас есть много информации, которую должен знать ваш игрок? Правильно, вы размещаете эту важную информацию об игроке на самом персонаже. Таким образом, информация на экране становится от третьего лица, позволяя игроку по-прежнему видеть самое важное на экране и его окружение. Индикаторы здоровья Айзека находятся на шкале здоровья, встроенной в подсвеченные узлы на его позвоночнике, как показано на рис. 8.2, а счетчик стазиса встроен в его правую лопатку в виде частично светящегося круглого кольца. Теперь игроку не нужно отрывать взгляд от главного героя, чтобы узнать его здоровье и характеристики.
162 Пользовательский интерфейс и меню
Рис. 8.2. Визуализация здоровья персонажа в Dead Space
Недиегетический – повествовательное «нет», внутреннее «нет» Глядя на сетку, вы можете подумать, как у вас может быть пользовательский интерфейс, которого нет в повествовании или игровом пространстве? Это отличный вопрос, и он встречается чаще, чем вы думаете! Практически каждая система меню и неинтегрированный проекционный дисплей (HUD – heads-up display) не являются диегетическими, как показано на рис. 8.3. Какие-либо взаимодействия через нажатия кнопок в игре не является частью игрового повествования, но это часть игры, и важная часть.
Рис. 8.3. Недиегетический HUD в Forza
Пользовательский интерфейс 163 Давайте подумаем о компасе и посмотрим, сможем ли мы превратить его в недиегетический элемент UI. Его цель – помочь игроку понять, куда идти. Можем ли мы сделать это так, чтобы игровые персонажи никак не взаимодействовали с компасом? Вы можете сделать мини-карту, показывающую направление, в котором должен двигаться игрок, и придать ей форму компаса на экране. Когда эта идея пришла нам в голову, мы решили, что да, компас возможно преобразовать в недиегетическую форму. Существует так много примеров недиегетического элемента пользовательского интерфейса в производстве, но один из наших любимых – это UI гоночной игры. Forza имеет понятный пользовательский интерфейс, показывающий механическую передачу, в которой вы находитесь, скорость и местоположение в мире на миникарте, чтобы помочь вам в пути.
Пространственный – повествовательное «нет», внутреннее «да» Вот забавный пример дизайна пользовательского интерфейса. Пространственные UI существуют в игровом мире, но персонажи внутри игры не знают об их существовании. Взглянув еще раз на компас, может быть, мы хотим, чтобы он был пространственным. Как мы можем передать направление, в котором нам нужно двигаться, так, чтобы персонаж не знал об этом? На земле может быть спроецированный компас, показывающий направление к следующей путевой точке или цели. Он появляется только тогда, когда вы смотрите сверху вниз от своего персонажа, поэтому он не всегда мешает игровому процессу в целом. Один из лучших пространственных элементов пользовательского интерфейса в игре – Path of Exile. Предметы, лежащие на земле, имеют цветной шпиль для обозначения определенных типов предметов, а их названия дают описание того, чем они могут быть, как показано на рис. 8.4.
Рис. 8.4. Названия пространственных предметов в Path of Exile
164 Пользовательский интерфейс и меню Персонаж находится в игровом пространстве, но он не знает об этих названиях, поэтому вам нужно навести на него указатель мыши, чтобы увидеть название.
Мета – повествовательное «да», внутреннее «нет» Метапространство интересно для UI, поскольку у нас не может быть интерфейса в игровом мире, но персонаж должен знать его. Если мы посмотрим на наш пример с компасом и попытаемся преобразовать его в метапространство, нужно хорошенько подумать. Разрушение четвертой стены путем непосредственного взаимодействия с пользователем, когда персонаж знает о сценарии, довольно уникально. Давайте попробуем. Градусы компаса находятся на внешней его части и следуют за поворотами персонажа. Персонаж смотрит на свой компас, и вы можете видеть, что направление близко или не близко к правильному местоположению. Это затруднительно и не кажется интуитивно понятным. Гораздо лучший пример меты – шутеры от первого лица. Вы когда-нибудь играли в шутер от первого лица и получали урон? Экран становится красным, и повсюду брызги крови. Персонаж знает, что ему нанесли урон, и обычно издает звук; метаинтерфейс позволяет игроку узнать о возможности смерти, если он продолжит получать удары. Камера, показывающая игрока, не является его зрением, и мы это знаем, но наш кровавый рендеринг, который темнеет и усиливается, дает ощущение драматической тревожности конца жизни.
Рис. 8.5. Мета-UI на экране в Call of Duty
То, что мы только что обсудили, – это методологии проектирования, позволяющие понять, что ваши элементы пользовательского интерфейса отображают для пользователя. Соответственно, существует несколько способов классифицировать элемент UI. Теперь мы рассмотрим некоторую общепринятую терминологию в разработке пользовательского интерфейса для игр.
Элементы UI 165
Элементы UI Существуют общие элементы пользовательского интерфейса, которые используются во время любой игры. Будь то главное меню, система инвентаря, отображение состояния здоровья или система взаимодействия с пространственными предметами. Все они служат одной цели: предоставить игроку как можно больше информации, не слишком сильно влияя на его погружение, и не отвлекать от игрового процесса. В следующих нескольких разделах мы рассмотрим темы, упомянутые ранее. Это общие термины для UI, и их не следует конкретизировать. Это дизайнерские мысли по общим темам, которые в настоящее время существуют для части пользовательского интерфейса в мире разработки игр.
Главное меню Когда игровое меню появляется впервые после загрузки игры, это первый эмоциональный отклик. Ваша игра хоррор? Шрифт и изображения должны отражать это. Есть много способов настроить меню. Нужно ли всплывающее оповещение, прежде чем вы выберете своих персонажей при входе в систему? Перескакивает ли главное меню прямо в игровой процесс при нажатии кнопки Play? Нужно ли иметь несколько уровней меню, поскольку игра ориентирована на системы меню? Все это отличные вопросы. Самый простой способ создать систему меню – убедиться, что играть в игру с предполагаемой сложностью или подключением, если это многопользовательская игра, несложно. Нам нравится называть это «низким входным барьером». Если игрок хочет войти и нажать кнопку воспроизведения, не заглядывая в настройки, он должен иметь возможность сделать это. Опыт игрока не должен зависеть от понимания того, с чем может справиться его система. Хороший способ подумать об этом – игровой опыт на аркадных автоматах или консолях. PlayStation и Xbox требуют от разработчиков игр обеспечение высокой частоты кадров, чтобы опыт был на высоком уровне. Это также должно относиться к ПК и мобильным устройствам.
Инвентари Существуют и другие формы систем меню, похожие по своей природе, но не являющиеся частью первоначального опыта игрока. В ролевых играх (RPG – role-playing game) часто используется система инвентаря, которая показывает, что вы храните на своем персонаже в виде доспехов или любого другого снаряжения. Это можно использовать в качестве конкретного места в мире, чтобы заставить игроков вернуться в город для продажи или улучшения своего снаряжения. Его также можно использовать для определения опыта, поскольку персонаж не может одновременно носить 30 комплектов доспехов и 200 видов оружия, бродя по миру. Это попытка преодолеть грань между нарушением погружения и контролем реализма. Некоторыми интересными формами систем инвентаря являются журналы квестов и достижения. Журнал квестов – это просто список квестов, которые можно выполнить или закрыть их выполнением. В случае с достижениями вы получаете их, выполняя определенные задания.
166 Пользовательский интерфейс и меню
Здоровье Здоровье персонажа может быть представлено «оставшимися жизнями», как в Super Mario Bros. Также может быть представлено тем, сколько еще раз вы можете получить урон, например как в Dead Space. Это может быть даже не конкретное значение, а количество крови на экране, как в Call of Duty. Еще более абстрактным является не здоровье, а оставленный на экране таймер того, сколько времени у вас осталось на выполнение квеста или уровня. Все это можно считать представлениями о здоровье, и они могут выглядеть на экране поразному, используя любую из форм, о которых мы говорили ранее.
Система взаимодействия с предметами В вашей игре могут быть предметы, с которыми игроку нужна помощь для получения понимания, что с ними можно взаимодействовать. Есть два основных способа проработать это, и оба они могут быть пространственными. Иногда это будет недиегетическая форма, о чем мы поговорим далее. Один из способов – сделать всплывающую подсказку на экране, которая будет доступна только тогда, когда ваша мышь или прицел охватывают элемент. Обычно это происходит, когда вы хотите, чтобы что-то было контекстно связано с этим конкретным элементом. Это можно сделать, когда всплывающая подсказка находится в пространстве экрана – это означает, что она всегда одного размера и больше похожа на плавающее окно. Это та же концепция, которая была показана на изображении Path of Exile выше. Еще вы можете увидеть плавающий значок вокруг или над элементом в контексте, возможно, для того, чтобы игрок знал, что он может с чем-то взаимодействовать. Способы похожи по своей природе, но экранное пространство означает, что оно не является частью мира, и персонаж также не осознает этого. Это делает его недиегетическим. Второй пример значка, плавающего над предметом, является пространственным, поскольку он показывает себя в мире, но персонаж об этом не знает.
UI в нашем проекте Наш проект не перегружен пользовательским интерфейсом. Мы намеренно хотели сделать его как можно более легким, чтобы создать полное погружение в окружающую среду. Чтобы реализовать это, поговорим о трех основных частях: главном меню, меню выхода, пространственном UI. Для начала поговорим о главном меню и о том, как с него начинается наше погружение в игру с самого начала.
Главное меню Наше меню будет в первую очередь недиегетической системой. В первом меню Мивари будет в лесу смотреть в свою книгу. Меню будет располагаться слева с доступными для выбора заголовком, стартом игры и выходом. При нажатии кнопки Start Game происходит движение камеры с небольшой кинематографической анимацией, запускающей начало игры. Овладение нашим
UI в нашем проекте 167 персонажем происходит сразу после того, как Мивари начинает свою анимацию бездействия, как только синематик заканчивается нажатием кнопки Play. Такая система дает ощущение, что она уже с самого начала в меню находится внутри мира, поскольку камера не выключается для перехода между сценами, но она не является частью мира или частью повествования. Мивари не знает, что система меню существует, и оно никак не влияет на игровой мир, поэтому меню не является диегетическим. Скриншот, который мы показываем ниже на рис. 8.6, – макет, иллюстрирующий логику без необходимости в невероятном искусстве. Это обычная тактика при разработке игр. В следующих разделах мы рассмотрим реализацию фактического пользовательского интерфейса.
Рис. 8.6. Макет главного меню
Нам нравится концепция UI, которая позволяет игроку почувствовать, что игра, в которую он играет, сразу же иммерсивна. Когда вы нажмете стартовую кнопку, меню должно исчезнуть, а камера должна переместиться в положение, в котором вы затем возьмете на себя управление главным героем. Цель состоит в том, чтобы не было загрузочного экрана. Максимально вовлекайте игроков.
Меню выхода Чтобы обеспечить максимальное погружение, мы хотели использовать одну из основных черт личности нашего персонажа: исследовательницу. Для нас это означает, что нужна книга, закрепленная на ее правом бедре, чтобы она была частью ее прохождения через игровой опыт. Мы также знали, что где-то нам понадобятся внутриигровые настройки, которые мы также могли бы разместить в книге. Это пространство в том смысле, что оно нарушает погружение в игру, поскольку настройки не являются частью повествования. Когда Мивари откро-
168 Пользовательский интерфейс и меню ет раздел журнала Options, он покажется достаточно бессвязным, но будет знаком тем, кто привык играть в игры. Эта часть будет пространственной, поскольку она является частью мира, но Мивари не знает, что это меню для закрытия игры. На левой панели находятся все сюжетные элементы, которые являются частью мира, соответственно, и Мивари, и игрок используют их как подсказки для прохождения. Мы назовем эту часть меню диегетической, так как будем выбирать иллюстрацию так, как если бы эту книгу написал кто-то из расы Мивари. Сделаем это с помощью небольшого синематика, где Мивари вытаскивает и открывает книгу, которая будет обновляться в зависимости от того, где вы были в игре. Дизайн книги напоминает искусство, как будто писала в ней не она, а другой человек ее расы. Книга старая, и именно она привела персонажа в пещеру. Там будут небольшие заметки, которые помогут игроку в случае необходимости. У нас игра с линейным прогрессом, поэтому мы будем обновлять ее на каждой или промежуточной вехе. Если в какой-то момент персонаж долго стоит на месте, мы также попросим ее взять книгу и прочитать, что приведет к более тесному сближению погружения в книгу, являющуюся ее дневником, чтобы сохранить опыт как можно более соответствующим.
Рис. 8.7. Макет UI книги
Книга представляет собой интересную для нас систему меню. Она действует как меню выхода, а также дает игрокам больше подсказок о помолвке, о которой размышляет Мивари. На рис. 8.7 выше показан макет, который мы используем для визуализации того, как она может выглядеть. Так мы понимаем, куда поставить камеру, а также это помогает аниматору узнать, как анимировать действие, когда она достает книгу из кобуры.
Пространственная подсказка При разработке обратной связи с игроком есть довольно много вариантов, как мы видели в задаче с компасом. В нашем случае мы думали о том, как лучше всего показать способность взаимодействовать с окружающей средой.
Unity UI 169 Мы остановились на пространственной системе. Эта система будет иметь форму так называемой всплывающей подсказки. Эта всплывающая подсказка представляет собой небольшую иконку, которая находится в пространстве мира над игровым объектом, с которым может взаимодействовать игрок. Мы решили использовать пространственную систему, чтобы сохранить элемент в мире для пространственного контекста элемента UI; однако мы не хотели, чтобы это было частью повествования. Так, мы используем малейшее нарушение погружения, чтобы резко контрастировать с остальной игрой. Когда игрок увидит всплывающую подсказку, будет интересно ее прочитать. Можно использовать данную систему по всему вертикальному срезу! Мы создаем пример простого ключевого предмета, который будет значком, плавающим в игровом мире, но Мивари не будет знать о его существовании. Это позволяет нам создать надежную систему; если мы решим использовать другую кнопку для другого типа взаимодействия, можем просто изменить значок для правильной кнопки, которую нужно нажать.
Рис. 8.8. Макет пространственного UI
Ярко-розовый кружок – просто заполнитель элемента, который позже станет нашим индикатором. Благодаря тому, что он ярко-розовый, вы не спутаете его с чем-то другим! Итак, мы рассмотрели определение пользовательского интерфейса и объяснили его использование в нашем проекте. Теперь необходимо рассмотреть, как мы на самом деле заставили UI работать.
Unity UI Прежде чем полностью погрузиться в нашу реализацию UI проекта, рассмотрим основы системы пользовательского интерфейса Unity. Это даст вам представление о том, какие элементы мы используем в системах, а также о паре элементов, которые мы не используем и которые вы могли бы использовать в своих проектах позже. Есть две основные части, чтобы сделать эту работу: система Unity Canvas, компоненты Unity UI.
170 Пользовательский интерфейс и меню Перед реализацией кода нам нужно подробно рассмотреть систему Unity Canvas, чтобы у вас была хорошая основа понимания ее внутренней работы, прежде чем пытаться добавить к ней графику.
Система Unity Canvas Unity размещает свой пользовательский интерфейс внутри системы холста (canvas). Это GameObject, который по умолчанию содержит несколько компонентов. Чтобы создать холст, щелкните правой кнопкой мыши в окне Hierarchy и выберите UI, затем Canvas. Взгляните на рис. 8.9 ниже.
Рис. 8.9. Меню для создания холста
Далее, у вас будет GameObject Canvas и GameObject Event System. Если на том уровне уже есть Event System, будет создан только Canvas. Canvas имеет преобразование Rect, а также компоненты Canvas, Canvas Scalar и Graphic Raycaster. Подробно рассмотрим каждый из них. Существует также Event System, которую можно было бы создать, если бы в иерархии сцен не было другой. Здесь будут размещаться сообщения для ввода в UI. Если вы используете новую систему ввода, не забудьте нажать на нее и заменить StandaloneInputModule на InputSystemUIInputModule. Это позволяет системе событий знать, какие системы ввода работают в проекте.
Unity UI 171 Почему бы нам не рассмотреть по порядку компоненты, начиная с преобразования Rect, Canvas, Canvas Scalar и затем более подробно про Graphic Raycaster?
Преобразование Rect У самого холста есть преобразование Rect, но оно должно быть родителем другого пользовательского интерфейса, поэтому его преобразование Rect должно быть доступно только для чтения. Щелкните правой кнопкой мыши по холсту и выберите UI > Button, чтобы можно было создать дочернюю кнопку внутри холста и ясно видеть преобразование Rect. Ниже на рис. 8.10 вы можете увидеть в инспекторе компонент преобразования Rect кнопки, где вы, возможно, ожидаете обычный компонент Transform. В нашем преобразовании Rect все еще есть параметры положения, поворота и масштабирования, но вдобавок у нас также есть ширина, высота, пивот и точки привязки. При работе с пользовательским интерфейсом лучше оставить масштаб 1, 1, 1. Это позволяет холсту устанавливать масштабирование, если необходимо. Самый безопасный способ изменить размер – через значения ширины и высоты. Вращение будет происходить от точки пивота, которая представляет собой маленький синий кружок и может быть изменена значениями полей Pivot.
Рис. 8.10. Компонент Rect Transform
Поля Pos будут устанавливать локальное местоположение GameObject. Когда вам нужно внести изменения в размер элемента UI, лучше использовать инструмент Rect вместо масштабирования. Внутри представления сцены есть кнопка инструмента Rect, показанная на рис. 8.11 ниже, позволяющая изменить размер пользовательского интерфейса и обновить положение, ширину и высоту.
Рис. 8.11. Инструмент Rect используется для выбранной кнопки
Пивот элемента UI – это значение X или Y, которое является нормализованным значением ширины и высоты элемента. Это означает, что значение
172 Пользовательский интерфейс и меню 0.5 в обоих случаях поместит ось на 50 % ширины и высоты или в локальный центр объекта. Последний уникальный предмет – точки привязки (anchors). Точки привязки предназначены для того, чтобы элементы UI оставались на месте, даже если масштабируется холст. Это может произойти, если вы используете несколько устройств одновременно с разными разрешениями. У аnchors существуют параметры Min/Max, которые будут устанавливать для каждой точки соответствующее нормализованное значение, аналогичное местоположению точки пивота. Выполнение этого вручную занимает немало времени, поэтому у нас есть удобный инструмент, чтобы упростить себе жизнь. Если вы щелкнете в верхнем левом углу по Rect Transform, откроется полезный инструмент, который позволяет вам выбирать из общих параметров точек привязки.
Рис. 8.12. Общие параметры точек привязки
Этот инструмент позволяет вам выбирать наиболее распространенные позиции привязки для игрового объекта, над которым вы работаете. Существует два основных типа привязки: Positional и Streched. Сетка 3×3 в середине этого инструмента сделает так, чтобы рассматриваемый пользовательский интерфейс был закреплен, а не растягивался или менялся, когда разрешение экрана отличается от того, для которого вы создали этот интерфейс. Это хороший вариант, только если разрешение не кардинально изменится. Второй тип – растяжка, которая располагается по правому и нижнему краю. Если ваша игра создана с разрешением 1920×1080, а игрок предпочитает играть на сверхшироком мониторе, вы можете разрешить некоторое масштабирование определенных элементов пользовательского интерфейса. Если это монитор 4К с соотношением сторон 16:9, то вам нужно будет подумать о растягивании всех ваших элементов; в противном случае пользовательский интерфейс будет казаться очень маленьким.
Unity UI 173 Точки привязки – это что-то вроде искусства. Уловки, которые были изложены выше, обязательно вам пригодятся. Лучший способ добиться правильной привязки – запустить игру в редакторе и изменить ее размер. Это даст вам хорошее представление о том, как элементы пользовательского интерфейса реагируют на изменение разрешения.
Компонент Canvas Компонент холста содержит лишь несколько параметров, но они имеют решающее значение! На рис. 8.13 ниже вы можете увидеть разделы, которые мы пройдем далее.
Рис. 8.13. Компонент Canvas
У нас есть Render Mode с несколькими опциями под ним: Pixel Perfect, Sort Order и Target Display. После этого у нас есть Additional Shader Channels. Давайте рассмотрим эти варианты по порядку. Render Mode Можно выбрать три режима рендеринга: Screen Space – Overlay, Screen Space – Camera и World Space. Каждый из них имеет определенный тип использования, и игры могут иметь несколько холстов в своем мире, которые соответствуют их потребностям. Когда мы их пройдем, самостоятельно подумайте, как мы могли бы использовать их в нашем текущем проекте. После того как мы опишем все возможности пользовательского интерфейса Unity, перейдем к реализации. Screen Space – Overlay Это обычный режим рендеринга холста. Что хорошо в этом режиме, так это то, что его можно использовать в своей собственной сцене и дополнительно загружать в вашу игру во время выполнения. Это позволяет легко создавать мобильные меню, которые отделены от систем меню монитора ПК. Отлично, однако его следует использовать только с простым пользовательским интерфейсом. Если вы собираетесь перетаскивать элементы пользовательского интерфейса или анимировать их из контекста мыши, например, при наведении курсора, то лучше всего использовать параметр Camera. Хорошим примером такого типа холста является главное меню или HUD, которые не очень интерактивны. Screen Space – Camera Как и в случае с наложением (overlay), это отличный режим, если вы собираетесь создавать функции, использующие класс EventTrigger. Вы также не можете создать такой экземпляр, как режим наложения. Он уже должен быть в сцене и иметь камеру, на которую он будет ссылаться для определения границ. Он прикрепится к камере, поэтому, если вы внесете это изменение и он исчез-
174 Пользовательский интерфейс и меню нет, дважды щелкните по камере, и он тут же появится! Отличным примером этого режима является что-то похожее на ARPG, где вам нужно перетаскивать снаряжение, чтобы экипировать объекты. World Space Данный же режим рендеринга для холста используется, когда вам нужно меню, которое находится в мировом пространстве. Простой способ пояснить – через лучшие варианты использования. Вы можете использовать режим, когда хотите, чтобы над головой персонажа в пространстве появились «пузыри» чата. Вам могут понадобиться выбираемые ориентиры в пользовательском интерфейсе, которые потенциально могут использовать холст World Space. Было бы лучше, если бы к этому ориентиру был прикреплен текст или другая форма пользовательского интерфейса. Параметры Render Mode Под Render Mode есть три параметра. Pixel Perfect – используется только в том случае, если вы работаете в 2D-пространстве, где пользовательский интерфейс должен быть точным для каждого пикселя. Это помогает разработать UI с учетом ограничений пикселей при создании. Sort Order – по умолчанию порядок сортировки установлен для работы через иерархию под Canvas. Чем выше элемент находится в иерархии, тем быстрее он будет отображен. Вы можете перезаписать свое значение. Меньшие значения будут отображаться первыми. Более высокие значения отправляются ниже по списку. Это полезно, если вы хотите, чтобы один элемент всегда отображался последним. Просто введите 999 в качестве значения, и оно всегда будет отображаться после остальных независимо от порядка иерархии. Target Display – следует использовать, если вам нужен другой пользовательский интерфейс для второго дисплея. Вы можете настроить его, например, для отображения только на втором дисплее. Такая возможность есть для использования восьми дисплеев. Обычно данный режим используют при разработки гоночных игр, на трех изогнутых мониторах. Additional Shader Channels При наложении пользовательский интерфейс обычно не включает нормали, тангенты и т. д. Используйте раскрывающийся список, показанный на рис. 8.14, чтобы выбрать их.
Рис. 8.14. Опции Additional Shader Channel
Unity UI 175 Их нужно будет выбрать, если они вам особенно нужны в элементах пользовательского интерфейса. В противном случае оставьте его на Nothing.
Canvas Scaler Компонент отвечает за правильность масштабирования всех дочерних объектов пользовательского интерфейса в GameObject с прикрепленным этим компонентом. Он отвечает не только за масштабирование самого пользовательского интерфейса, но также за размеры шрифта и любые прикрепленные к изображениям границы. Компонент Canvas Scaler имеет несколько уникальных параметров. Они размещаются в окне в зависимости от того, какой из режимов масштабирования пользовательского интерфейса выбран. Существует три режима масштабирования UI. Constant Pixel Size Режим используется, когда вам нужно сохранить размер пикселя одинаковым независимо от изменения экрана. На практике применяется, если вы знаете, что будете играть в эту игру с одним разрешением. Если ваша игра вообще может быть масштабирована, то вы должны работать с динамической установкой коэффициента масштабирования и обеспечением того, чтобы ваши пиксели на единицу были одинаковыми. Эти параметры показаны на рис. 8.15 ниже.
Рис. 8.15. Компонент Canvas Scaler и Constant Pixel Size в UI Scale Mode
Если вы считаете, что ваша игра может быть скорректирована в любой момент, рассмотрите возможность работы с параметром Scale With Screen Size. Scale With Screen Size Когда вы выбираете Scale With Screen Size, появляются другие параметры, нежели при выборе Constant Pixel Size. Как видно ниже на рис. 8.16, у нас есть Reference Resolution, Screen Match Mode, ползунок Match и Reference Pixels Per Unit.
Рис. 8.16. Компонент Canvas Scaler и режим Scale With Screen Size
176 Пользовательский интерфейс и меню Reference Resolution – разрешение, которое вы ожидаете от наиболее часто используемого разрешения экрана. Оттуда он будет уменьшаться или увеличиваться в зависимости от разрешения, с которым могут играть игроки. Screen Match Mode – содержит три параметра: Match Width Or Height – это позволит приложению сопоставлять сочетание ширины и высоты при изменениях. В целом это работает довольно хорошо, пока вы не наткнетесь на сверхширокие мониторы. Это также единственный вариант, в котором ползунок Match доступен для изменения. Со следующими двумя параметрами этот ползунок не будет активен; Expand – это означает, что холст будет увеличиваться, но не будет меньше Reference Resolution. Это превосходно, ведь именно так расширяются потребности в ширине или высоте. Данный вариант, безусловно, мой любимый для работы; Shrink – этот параметр аналогичен параметру Expand, но он будет уменьшаться и не станет больше, чем Reference Resolution. Это хорошо работает, если вы изначально работаете с большим разрешением. Reference Pixels Per Unit – параметр ссылается на количество пикселей в сантиметре (единице Unity). На это очень важно обращать внимание при создании 2D-игры со спрайтами. Если для вашего спрайта установлено значение 100 пикселей на единицу, а для этого Reference Pixels Per Unit установлено значение 50, ваш спрайт будет в два раза больше, чем нужно. Constant Physical Size Напоминает режим Constant Pixel Size, однако работает с физическими единицами, как показано ниже на рис. 8.17. Вам может быть удобно задавать размеры в этих единицах, а не в пикселях.
Рис. 8.17. Компонент Canvas Scaler и режим Constant Physical Size
Если вам лучше использовать эти единицы, убедитесь, что вы изменили масштаб всех ваших шрифтов соответственно. Список физических опций показан ниже на рис. 8.18.
Рис. 8.18. Параметры физических единиц измерения
Unity UI 177 Работа с любым из этих параметров заставит вас изменить все элементы пользовательского интерфейса, чтобы они соответствовали единицам масштабирования одного и того же типа. Например, размер в пикселях обычно составляет 300 в ширину, тогда как 300 см – это очень много! Масштаб, вероятно, должен быть в значении 0.1. По этой причине мы рекомендуем вам работать со своими системами и знать, какую из них вы будете применять, если хотите использовать этот режим масштабирования с самого начала. Последний компонент – Graphic Raycaster. Это предпоследний элемент по умолчанию, который поставляется с холстом. Давайте разберемся, как Graphic Raycaster работает с холстом.
Компонент Graphic Raycaster Этот компонент создается на холсте. Цель его состоит в том, чтобы быть функцией того, на что щелкает ваша мышь. Ниже представлены доступные параметры Graphic Raycaster (рис. 8.19):
Рис. 8.19. Компонент Graphic Raycaster
Здесь есть три параметра, которые нужно быстро пояснить. Ignore Reversed Graphics – этот параметр гарантирует, что вы не сможете щелкнуть по перевернутым объектам. Помните, что обратные стороны отбраковываются в камере. Вы можете переворачивать элементы пользовательского интерфейса, но они по-прежнему будут доступны для кликов, если этот флажок не установлен. Blocking Objects – позволяет элементам, которые находятся в 2D или 3D над пользовательским интерфейсом, блокировать нажатие на пользовательский интерфейс. По умолчанию значение none. Blocking Mask – параметр позволяет размещать слои, чтобы заблокировать пользовательский интерфейс. Поскольку UI – это спрайты, они обычно представляют собой прямоугольник и могут довольно легко перекрываться. Чтобы обойти это, вы можете создать блокирующий слой UI, который позволит вам размещать объекты впереди, чтобы блокировать щелчок, даже если он невидим, с альфа, равным 0. Мы выделили время на эти стандартные вещи, поскольку они являются основой в начале работы с пользовательским интерфейсом Unity. Есть еще несколько вариантов, которые нужно изучить со временем, когда вы будете создавать больше UI, но данная здесь основа поможет вам начать работу. Далее мы рассмотрим некоторые объекты UI, которые можно добавить на холст.
178 Пользовательский интерфейс и меню
Объекты пользовательского интерфейса Unity Теперь у нас есть холст! Мы узнали, как он работает с динамическим разрешением и как настроить его для нужд вашей игры. Далее нам нужно добавить некоторые объекты. Объекты пользовательского интерфейса Unity делятся на два типа: визуальные и интерактивные. Визуальные элементы – это то, что вы ожидаете увидеть. Это элементы, которые могут быть исключительно визуальными, но также они могут быть прикреплены к интерактивным элементам. Ниже приведены примеры этих объектов, включая описание и наглядный пример. Image. Существует два типа изображений: Raw Image и Image. Первый вариант Raw Image используется только там, где вам не нужна граница; однако, как правило, лучше просто использовать объект Image. К ним можно добавить спрайты изображений и рамку. Вы также можете подкрасить спрайт в компоненте Image в инспекторе. Существует также еще один вариант UI под названием Panel. Это еще один объект UI с прикрепленным компонентом изображения, предназначенным для панели UI. Единственная разница между Image и Panel заключается в том, что Panel по умолчанию будет растягиваться и заполнять весь холст.
Рис. 8.20. Image и компонент UI Image
Mask. Компонент Mask вырезает находящиеся под ним игровые объекты. Это отлично подходит для маскировки дополнительных предметов под ним, которые, возможно, не должны быть видны. Ниже мы добавили маску к изображению и еще одно изображение под ним. На рисунке контур – это маска; изображение, которое должно быть квадратным, обрезается сверху и снизу из-за маски, скрывающей его.
Рис. 8.21. Замаскированное по умолчанию изображение с рис. 8.20
Text – это, очевидно, текст! Иногда его также называют Label. Вы можете добавить определенный шрифт в свой UI, если нужно. Когда вы создадите его, то увидите компонент TextMeshPro после текстовой опции. Это связано с тем, что TextMeshPro (TMP) настолько часто используется, что был интегрирован в основные функции Unity.
Unity UI 179
Рис. 8.22. Компонент UI TextMeshPro
Интерактивные элементы могут содержать визуальные элементы, но они поставляются с интерактивными событиями UnityEvents. Ниже приведены их примеры с описанием. Button – этот интерактивный объект по умолчанию имеет метку в своей иерархии и поставляется с UnityEvent при нажатии. Он имеет возможность окрашиваться при выделении, нажатии или отключении. Это основная функция UI с взаимодействием.
Рис. 8.23. Компонент UI Button
Dropdown – это выбираемое пользователем поле из предопределенной группы параметров. Когда пользователь изменяет это значение, он вызывает событие OnValueChangedUnityEvent.
Рис. 8.24. Компонент UI Dropdown
Input Field – это стандартное поле ввода, в которое пользователь щелкнул или «сфокусировался» на нем. Мы хотели бы упомянуть об интересном свойстве Content Type – оно позволяет разработчику проверять ошибки без необходимости написания кода. Например, если установить для этого параметра значение Integer Number, пользователь сможет вводить только числа. Этот интерактивный объект имеет два события UnityEvent: OnValueChanged – будет возвращать строку того, какое значение в настоящее время находится во входном значении каждый раз, когда происходит изменение; EndEdit – вернет строку, когда пользователь щелкнет где-нибудь еще или иным образом потеряет фокус на этом поле ввода.
Рис. 8.25. Компонент UI Input Field
180 Пользовательский интерфейс и меню Scrollbar – обычно используется в сочетании с Scroll Rect. Его цель – быть полосой прокрутки для поля, если вам нужно что-то большое. Значение может быть от 0 до 1, независимо от размера полосы прокрутки, которая может быть вертикальной или горизонтальной. Имеется также событие UnityEvent, которое можно использовать для определения OnValueChanged, чтобы вы могли вернуть значение при перемещении полосы прокрутки.
Рис. 8.26. Компонент UI Scrollbar
Scroll Rect – это также можно назвать представлением полосы прокрутки. Здесь можно сделать две полосы, чтобы при необходимости настроить вертикальную и горизонтальную прокрутку. Настраивается с помощью маски, чтобы скрыть информацию за пределами самой маски. Он также имеет UnityEvent OnValueChanged при прокрутке Scroll Rect.
Рис. 8.27. Компонент UI Scroll Rect
Slider – это ползунок с перетаскиваемым объектом, который будет устанавливать значение ползунка от минимального до максимального значения, установленного вами. У него также есть событие UnityEvent, возвращающее значение из этого минимального и максимального значения OnValueChanged.
Рис. 8.28. Компонент UI Slider
Toggle – это флажок, которому назначена метка. При нажатии вы можете использовать UnityEvent OnValueChanged, чтобы оценить, включено оно или нет.
Рис. 8.29. Компонент UI Toggle
Unity UI 181 Toggle Group – если добавить переключатели в группу, вы можете настроить эту группу, чтобы разрешить выбор только одного из них. Если выберете другой в назначенной группе, он выключит ранее включенный переключатель и включит выбранный переключатель. Существует параметр Allow Switch Off, который позволяет выбрать в данный момент выбранный переключатель, чтобы ни одна из групп не была выбрана. Уникального события UnityEvent, связанного конкретно с группой, нет; однако каждый переключатель по-прежнему имеет свое собственное событие OnValueChanged, которое срабатывает. Одно небольшое замечание: если вы собираетесь создать группу переключателей, убедитесь, что каждому переключателю назначен Group в компоненте Toggle.
Рис. 8.30. Компонент UI Toggle Group
Все это примеры элементов UI, доступных для Unity. Отсюда нам нужно пройти через реализации пользовательского интерфейса Unity, чтобы он соответствовал нашей игре. Ранее мы рассмотрели дизайн; теперь нам нужно заглянуть в код, чтобы увидеть, как он работает, когда игрокам нужно взаимодействовать с интерфейсом.
Реализация Теперь посмотрим на нашу реализацию. Знание того, как выглядят все объекты пользовательского интерфейса и их назначение, безусловно полезно, но давайте разузнаем, каковы они на практике. Мы начнем с главного меню перед началом игры. После этого прорвемся в журнал или меню выхода. Затем мы закончим с пространственным пользовательским интерфейсом для взаимодействия с игровой механикой. Читая эту часть, помните, что мы не будем рассматривать все строки скрипта, так как на этом этапе предполагаем, что вы освоились с кодом, который у нас есть на GitHub. Если в какой-то момент вы почувствуете, что сбиты с толку тем, как книга составлена для объяснения кода, проработайте все скрипты на GitHub. Основная цель нашего объяснения кода в таком формате – максимально кратко рассказать о том, что мы делаем и почему. Тем более что видение каждой строки кода не поможет в этом! Перейдем к реализации главного меню.
Реализация главного меню Поскольку мы хотели, чтобы это меню было недиегетическим, а находилось в мировом пространстве и создавало иллюзию пространства, мы решили использовать холст World Space. Ниже на рис. 8.31 показана иерархия и инспек-
182 Пользовательский интерфейс и меню тор со свернутыми компонентами, которые не отличаются от значений по умолчанию.
Рис. 8.31. Слева иерархия для MainMenuCanvas; справа инспектор холста
Скрипт MainMenuUIControl.cs – это то, как мы будем управлять нашим главным меню. При работе с пользовательским интерфейсом вам необходимо убедиться, что вы импортируете его библиотеку: using UnityEngine.UI;
Использовав библиотеку пользовательского интерфейса, вы сможете получить доступ ко всем его объектам и их методам. Хотя следующая строка, которую я хотел бы разместить здесь, не является частью пользовательского интерфейса, хотелось бы показать вам кое-что, о чем мы еще не говорили. Метод называется FindObjectOfType. Мы знаем, что в сцене всегда будет только один класс MyvariThirdPersonMovement, поэтому используем этот метод, чтобы получить класс, а затем запрашиваем его родителя, чтобы мы знали player root. playerRoot = FindObjectOfType().transform.parent;
Нам также нужно отключить персонажа и настроить прослушиватели для системы событий, чтобы она знала, что делать, когда мы нажимаем кнопки на холсте. Чтобы отключить персонажа, у нас есть метод on awake, который мы вызываем, чтобы отключить то, что нужно. При использовании Cinemachine необходимо отключить все доступные камеры, иначе Cinemachine переключится на одну из камер. Затем отключаем только скрипт управления игрока. Это позволяет анимации персонажей продолжать воспроизводиться, но мы просто не можем ее контролировать. On awake: SetPlayerEnabled(false);
Unity UI 183 Отдельная частная реализация в строке 50: void SetPlayerEnabled(bool enable) { CinemachineVirtualCamera[] cams = playerRoot.GetComponentsInChildren(true); foreach (CinemachineVirtualCamera cam in cams) { cam.gameObject.SetActive(enable); } playerRoot.GetComponentInChildren().enabled = enable; }
Мы уже настраивали слушателей несколько раз, но давайте взглянем и на них: startGameButton.onClick.AddListener(OnStartGameButtonPressed); quitButton.onClick.AddListener(OnQuitButtonPressed);
Мы сделали так, что соответствующие кнопки для startGameButton и quitButton будут активировать методы в своих слушателях при нажатии на них. Метод OnStartGameButtonPressed выглядит так: void OnStartGameButtonPressed() { SetPlayerEnabled(true); Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; this.gameObject.SetActive(false); }
Когда кнопка нажата, персонаж становится разблокированным, поэтому мы можем использовать ввод, чтобы перемещать ее, блокировать и скрывать курсор мыши и отключать главное меню, чтобы вы больше не могли его видеть. Если вы нажмете кнопку выхода, то закроете приложение. В Unity есть простой способ выйти из приложения: Application.Quit();
Вот мы и закончили с главным меню! Самая сложная часть во всем этом была заблокировать управление игрока. В противном случае персонажем можно было управлять, пока открыто главное меню, а в данном случае это не то, что нам нужно. Далее следует поработать над книгой Мивари.
Реализация книги В большинстве игр есть общая концепция меню выхода. То есть, нажимая клавишу Escape, вы открываете меню, которое обычно приостанавливает игровой процесс. В нашем случае мы хотели, чтобы при нажатии Escape персонаж открывал свою книгу и просматривал ее. Это было бы отлично, так как это позволяет игре приостанавливаться, когда камера приближается к книге, в которой можно разместить обычные параметры меню выхода, такие как
184 Пользовательский интерфейс и меню Вернуться в игру и Выход из игры. Здесь будут некоторые похожие концепции из главного меню, такие как блокировка и разблокировка курсора и разблокировка игрока. Ниже, на рис. 8.32, представлено другое представление иерархии и скрипта в инспекторе пользовательского интерфейса книги. Одним из уникальных элементов открытых полей является то, что мы используем систему ввода, а не полагаемся только на ввод с помощью мыши. Чтобы загрузить книгу, мы можем нажать букву B или клавишу Escape.
Рис. 8.32. Слева иерархия книги; справа панель инспектора книги
Интересно то, что все кодирование, связанное с данным скриптом, было выполнено ранее. Если нужно, вернитесь и вспомните предыдущие уроки кодирования. Последняя часть пользовательского интерфейса – это пространственный пользовательский интерфейс, который помогает игрокам понять, что предмет, на который они смотрят, доступен для взаимодействия. Давайте разберем данную реализацию.
Реализация UI-взаимодействия Уникальность настройки в том, что для этого элемента нет холста. У нас будет один GameObject, который находится в сцене и который мы переместим в нужное место и отключим или включим в зависимости от того, на что мы смотрим, если он предназначен для взаимодействия. Итак, есть простой GameObject, представляющий собой сферу без наложенного материала, поэтому он ярко-розовый.
Unity UI 185 В скрипте InteractiveHighlight.cs с помощью on awake мы находим этот GameObject и получаем его рендерер. Если он не найден, значит, есть ошибка. Мы берем средство рендеринга меша, чтобы при необходимости отключить его. void Awake() { highlightIndicator = GameObject.FindGameObjectWithTag("InteractionHigh light"); if (highlightIndicator == null) { Debug.LogError("Highlight indicator not found in scene"); } highlightIndicatorRenderer = highlightIndicator. GetComponent(); }
Теперь, когда есть индикатор подсвечивания объекта, мы должны выполнить скрытие и перемещение самого индикатора. Чтобы узнать, сталкиваемся ли мы с интерактивным предметом или частью кусочков головоломки игры, используем raycast. Это физический метод, поэтому мы поместим его в исправленное обновление. Так мы гарантируем, что код будет выполняться в соответствии со временем обновления физики, о котором мы говорили в главе 7. void FixedUpdate() { Ray ray = new Ray(transform.position, transform.forward); if (Physics.Raycast(ray, out RaycastHit hit, maxDistance, rayMask, QueryTriggerInteraction.Collide)) { highlightIndicator.transform.position = hit.transform.position + new Vector3(0f, height, 0f); if (!highlightIndicatorRenderer.enabled) { highlightIndicatorRenderer.enabled = true; } } Else { if (highlightIndicatorRenderer.enabled) { highlightIndicatorRenderer.enabled = false; } } }
Исправленное обновление здесь работает с синхронизацией физики и проверяет, попадает ли то, что передается из центра экрана, в объекты на двух масках. Если все сходится и оно находится на максимальном расстоянии, то переместите выделенный фрагмент и включите его визуализацию. Если нет, то отключите его!
186 Пользовательский интерфейс и меню
Заключение Восьмая глава была довольно насыщена информацией. Несмотря на то что у нас было только три части пользовательского интерфейса, все же нам нужно было разбить его на все эти части. Теперь у вас будет четкое представление о том, как другие разработчики игр разрабатывают свой пользовательский интерфейс для игр. В главе 12 «Последние штрихи» мы рассмотрим его наведение и то, как полировка пользовательского интерфейса влияет на опыт игрока. В следующей главе мы разберем визуальные эффекты и некоторые системы частиц.
9 Визуальные эффекты В настоящее время наша игра все еще не имеет всех своих функций. Мы перешли от концепции к блокировке, а затем довели игру до состояния, когда в нее можно играть. Но это не значит, что мы уже закончили! Нужно подумать, как можно сделать игру более эмоциональной. К счастью для нас, Unity предоставляет фантастические ресурсы и инструменты, чтобы мы могли взять текущее состояние игры и визуально улучшить ее еще на одну ступеньку. Это делается с помощью различных инструментов, таких как шейдеры, частицы и другие инструменты полировки, которые мы разберем в главе 12. Данные темы очень сложные. А пока рассмотрим основное внимание к визуальным эффектам, изучив шейдеры и частицы на высоком уровне. Затем перейдем к обзору их продвинутых форм. Поскольку для этого проекта мы используем Universal Render Pipeline (URP), рассмотрим важные инструменты, такие как Shader Graph, VFX Graph и Shuriken. Shader Graph визуально отображает детализацию и кодирование шейдеров. VFX Graph был создан, чтобы помочь вам понять различные свойства частиц графического процессора. Shuriken, инструмент для разработки частиц, ориентированный на процессор, доступен во всех пайплайнах рендеринга. Мы расскажем и об этом. В данной главе будут рассмотрены следующие темы: обзор визуальных эффектов, Shader Graph, системы частиц: Shuriken; VFX Graph.
Обзор визуальных эффектов В самом начале работа с визуальными эффектами может показаться пугающей. В настоящее время у нас есть простая сцена. Мир не будет казаться живым и захватывающим без времени, потраченного на преднамеренные ответы на вопросы, которые необходимо решить для развития повествования и дизайна игры. На протяжении всей этой книги вы работали над многими общими вопросами игрового дизайна. Вы смогли ответить на них сами и можете использовать их для любого проекта, над которым собираетесь работать. Также вам были
188 Визуальные эффекты даны ответы, которые мы нашли сами, создавая этот проект, и то, как он будет двигаться дальше. У нас было довольно хорошее представление о том, что будет чувствовать игрок: фэнтезийное исследование в самых простых терминах. Теперь пройдем через нашу сцену и найдем области, в которых нам нужно немного больше фэнтезийности. Исследование осуществляется с помощью механики и повествовательного дизайна. Фэнтези – это широкое понятие. Можно было бы поработать на самом деле с любой темой. Мы решили проработать эту расплывчатую отправную точку и нашли тему легкой научной фантастики, посвященную древней расе. Эти существа держатся за силу небесных тел окружающего их природного пространства. Работая с природой, они в какой-то момент построили пещеру, которую игрок будет исследовать. Нам нужно придумать способ воплотить это повествование через визуальный гейм-плей, и игрок, принимающий себя в качестве главного героя этого мира, – это то, к чему мы стремимся. Чтобы реализовать такое визуальное повествование, нужно включить несколько инструментов визуальных эффектов, доступных в Unity. Shader Graph позволяет нам создавать шейдеры, которые могут иметь интересные свойства, различными способами обрабатывающие свет и эффекты мерцания. Shuriken предоставляет системы частиц для добавления эффекта пыли, светящихся частиц вокруг биолюминесцентных растений и простых деталей других фантастических элементов. VFX Graph позволяет довести простые частицы до предела и задействовать системы GPU. Сделав это, VFX Graph даст нам возможность использовать большое множество частиц. Хотя это непрактично, вы сможете создать десятки миллионов частиц. Наконец, мы будем использовать освещение в Unity, чтобы подсказывать игроку, где искать, и задавать настроение и тон текущего действия, системы или места. В начале этой главы мы заложим основу терминов и подробно рассмотрим отдельные инструменты визуальных эффектов. После этих объяснений более подробно рассказажем о том, как мы включаем инструменты, которые Unity может предложить, в наше рабочее пространство для создания визуально захватывающей среды. В будущем этот раздел может стать полезной отправной точкой для повторного ознакомления с инструментами и их назначением.
Shader Graph Shader Graph – это визуальный инструмент для написания скриптов, позволяющий создавать шейдеры под руководством художника. Шейдеры – это скрипты, которые сообщают графическому конвейеру, как визуализировать материалы. Материал – это экземпляр шейдера с набором параметров для определенного меша внутри GameObject. Простым примером этого может быть кожа. Если вы думаете о коже, вы, вероятно, можете представить много различных типов кожи. Какого она цвета? Она блестящая? У нее уникальная текстура? Все эти типы являются параметрами шейдера, которые можно установить в материале для правильной визуализации объекта. В Unity шейдер написан на С-подобном языке высокого уровня для программирования шейдеров (HLSL – High-Level Shader Language). Знание того, как правильно написать такой код, требует подробного понимания графического
Shader Graph 189 конвейера на вашем компьютере, поэтому может быть немного сложно начать работу. Графический пайплайн представляет собой сложную концептуальную модель. Проще говоря, компьютер проходит через слои и этапы, чтобы понять, какая 3D-графика в сцене, затем берет эту информацию и отображает эти визуальные эффекты на двумерном экране. На самом деле простое прочтение абзаца выше может показаться немного запутанным. Это естественно. Здесь много движущихся частей, и мы разберем их в этой части главы. Чтобы не слишком углубляться в HLSL, мы сосредоточимся исключительно на Shader Graph. Если, проведя время в Shader Graph, вы хотите перейти на более техническую должность, мы рекомендуем изучить HLSL и рукописные шейдеры. Как только вы сделаете это, у вас будет прочная основа для создания общих шейдеров. Давайте сначала рассмотрим настройку Shader Graph, а после создадим шейдер. Затем мы должны уделить некоторое время разговору об основных и часто используемых нодах, которые используются для создания шейдеров. Ноды – это визуальное представление блока или фрагмента кода. Эти ноды могут быть связаны вместе для создания более крупных функций визуально многоуровневых эффектов.
Настройки Мы говорили об использовании проекта URP, на котором автоматически должен быть установлен Shader Graph. Если его нет, вы можете легко установить его, перейдя в Package Manage и установив его из пакетов реестра Unity. На рис. 9.1 ниже показано, что необходимо для правильной установки Shader Graph. Если у вас уже стоит зеленая галочка, как на рисунке, значит он уже установлен!
Рис. 9.1. Проверка наличия Shader Graph
Теперь, когда мы либо установили Shader Graph, либо убедились, что он уже имеется, давайте перейдем к созданию вашего первого шейдера.
190 Визуальные эффекты
Создание шейдера Щелчок правой кнопкой мыши в открытой области окна вашего проекта открывает меню для этого места. Мы хотим создать новый шейдер, поэтому наводим курсор на Create > Shader > Universal Render Pipeline и получаем четыре варианта. Эти четыре параметра являются основными в URP, которые автоматически настраивают параметры шейдера. Обратитесь к рис. 9.2, чтобы узнать, как создать свой шейдер.
Рис. 9.2. Путь для создания шейдера в редакторе Unity
Вы можете задаться вопросом: «Какой из них выбрать?» Это отличный вопрос. Давайте рассмотрим все четыре из этих типов на тот случай, если вы захотите повозиться с любым из других типов после того, как мы перейдем к нашему выбору. Сверху вниз у нас есть Lit, Sprite Lit, Sprite Unlit и Unlit Shader Graphs. Давайте рассмотрим каждый из этих типов в указанном порядке.
Lit Shader Graph Lit Shader Graph позволяет визуализировать 3D-объекты с информацией о реальном освещении. Шейдеры, использующие это, будут применять модель освещения на основе физического рендеринга (PBR – Physically Based Rendering). Модель PBR позволяет 3D-поверхностям иметь фотореалистичное качество материалов, таких как камень, дерево, стекло, пластик и металлы, при различных условиях освещения. С помощью Lit Shader освещение и отражения на этих объектах могут точно соответствовать динамическим сдвигам, таким как яркий свет в темной среде пещеры.
Sprite Lit Shader Graph URP поставляется с системой 2D-рендеринга и освещения. Эта система будет использоваться в этом типе графики, поскольку элементы, на которых будет отображаться этот шейдер, будут спрайтами. Спрайт – это двумерное растровое изображение (массив двоичных данных, представляющих цвет каждого пикселя), интегрированное в большую сцену. Это позволит спрайтам получать необходимую информацию об освещении.
Shader Graph 191
Sprite Unlit Shader Graph Это похоже на Lit Shader Graph выше, но разница заключается в том, что сцена всегда считается полностью освещенной и не принимает никакой информации об освещении. Этот график также использует только систему 2D-рендеринга и освещения в URP.
Unlit Shader Graph Неосвещенный (unlit) тип графики для URP использует PBR, как и тип Lit Shader Graph. Основное отличие состоит в том, что он не принимает информацию об освещении. Это самый производительный шейдер в URP.
Интерфейс Shader Graph Выберите тип Lit Shader Graph для Shader Graph. Если щелкнуть правой кнопкой мыши в окне проекта, будет создан новый файл шейдера. Двойной щелчок по этому файлу откроет окно Shader Graph. Следует рассмотреть подробнее некоторые детали, чтобы вы могли понять, что рассматривается в следующих подразделах. Нам нужно пройтись по Master Stack, Blackboard, Graph Inspector, Main Preview, а затем Nodes. На рис. 9.3 показаны четыре из пяти таких разделов. Мы подробно рассмотрим их в главе.
Рис. 9.3. Структура окна Shader Graph
Master Stack Элемент, обведенный зеленым цветом на рис. 9.3, – это Master Stack. В нем есть два раздела: Vertex и Fragment. Блок Vertex содержит инструкции для фактических вершин 3D-объекта, которыми будет манипулировать материал с этим назначенным шейдером. В этом блоке вы можете повлиять на атрибут вершины Position, Normal или Tangent. Эти три атрибута встречаются повсюду в 2D- и 3D-среде.
192 Визуальные эффекты Position представляет собой положение вершины в объектном пространстве. Normal используется для расчета направления, в котором свет отражается или излучается от поверхности. Tangent изменяет внешний вид вершин на поверхности, определяя горизонтальное (U) направление текстуры объекта. В нашем случае не нужно изменять какие-либо атрибуты вершин, поэтому мы перейдем к части фрагментного шейдера и оставим объектное пространство в покое. Фрагменты можно рассматривать как возможные пиксели на экране. Мы можем воздействовать на пиксели в зависимости от изменений, внесенных во входные данные стека. Атрибуты, перечисленные во фрагментном шейдере, зависят от типа шейдера, который мы выбираем при создании шейдера. Блоки внутри стека Fragment называются Fragment Nodes. Если вам не нужен конкретный Fragment Node, вы можете удалить его, щелкнув правой кнопкой мыши по Fragment Node по отдельности и выбрав Delete. Вы также можете добавить другие Fragment Node в стек, щелкнув правой кнопкой мыши по нижней части рамки Fragment и выбрав Add Node. На рис. 9.4 вы можете увидеть выбор всех вариантов узлов для добавления в стек. Если ваш шейдер не настроен на прием этих новых Fragment Node, они будут неактивными для использования. А пока давайте рассмотрим параметры Lit Shader Fragment.
Рис. 9.4. Параметры Fragment Node
Shader Graph 193 Fragment Node в стеке фрагментов представляют собой пиксели, которые потенциально будут отображаться в пространстве клипа или на экране. 3D-объекты также имеют 2D-представление своих лиц в виде UV. UV – это двумерное представление текстуры 3D-объекта. UV имеют плоскую двумерную ось, график от 0 (U) до 1 (V). Этот конкретный UV-график представляет собой представление каждой вершины на этой UV-плоскости, растянутой над 3D-объектом. UV-граф также называется UV-пространством текстуры. Глядя на рис. 9.5 ниже, вы можете видеть, что геометрия была развернута, чтобы сделать ее плоской. Это как бумажное ремесло или оригами. Знание того, как это работает, позволяет понять, как шейдеры могут манипулировать не только вершинами меша, но и цветом граней.
Рис. 9.5. Базовый меш, UV-развертка, градиент основного цвета
Мы хотели показать вам, как мы получили градиент в Shader Graph для рис. 9.5. Хотя это не самый простой график, с которого мы могли бы начать, он хорошо справляется с разбивкой некоторых ключевых понятий на раннем этапе. Если вы посмотрите на рис. 9.6, то увидите наш график для построения градиента, который мы поместили в базовый цвет. Затем градиент применяется к пространству 0–1 3D-объекта, которому мы назначили этот материал с помощью шейдера. Это несложный шейдер, так как он имеет жестко закодированные градиенты, исходящие из узла UV. Мы добавим больше сложности к параметрам в Blackboard в следующем разделе. Разберем часто используемые ноды в следующей части этой главы. На данный момент краткое объяснение того, что мы делаем, поможет разобраться в этом. Base color Мы берем UV-пространство 0–1, представленное здесь двумя градиентами, X и Y в красном и зеленом каналах. Красный и зеленый каналы являются частью цветового пространства; есть каналы Red (R), Green (G), Blue (B) и Alpha (A). RGB представляет значения цвета. Альфа указывает непрозрачность (opacity) каждого пикселя от 0 (полностью прозрачный) до 1 (непрозрачный). Мы видели, что в Unity пространство 0–1 начинается снизу слева и заканчивается линейно вверху справа. Это означает, что зеленый канал будет линейным
194 Визуальные эффекты
Рис. 9.6. Шейдер тестового градиента
градиентом снизу вверх. Разделение этого канала позволяет нам манипулировать 0–1 в ноде Lerp, при этом красный заменяет черный, а бирюзовый заменяет белый. В следующих нескольких разделах будет много всего, но держитесь! Когда мы разобьем ноды, будет намного проще следить за ними. Normal Нормали сообщают каждому фрагменту, как он должен реагировать на свет, падающий на плоскость. Это чрезвычайно полезно для добавления деталей к плоскости без изменения силуэта с уменьшением количества полигонов, необходимых для более высокой детализации. Глядя на рис. 9.7 ниже, вы можете видеть, что он выглядит так, как будто из куба вытянуты выпуклости. Это не изменение цвета; это свет, реагирующий на поверхность плоскости. Если присмотреться, по краям выступов нет. Это потому что форма куба не изменилась. Это просто куб, который действует так, как он есть, из-за карт нормалей. В левой части рис. 9.7 левый куб окрашен в синий цвет из-за того, что карта нормалей использует свою цветовую схему из каналов RGB в касательном пространстве. Это означает, что плоская карта нормалей будет представлять 0, 0, 1. Красный и зеленый каналы используются для представления изменений в том, как свет должен воздействовать либо на касательную X, либо на Y. Когда мы будем работать с материалами в главе 12 «Последние штрихи», более подробно остановимся на функциях карты нормалей.
Shader Graph 195
Рис. 9.7. Нормали куба
Metallic Metallic – это именно то, на что это похоже. Вот такой металлический материал! Однако это не лучшее определение, поэтому давайте попробуем немного его разбить. Металлическое поле представляет собой плавающую шкалу масштабирования от 0 до 1, где 0 не соответствует металлу, а 1 соответствует голому металлу. Металлические материалы принимают цвет окружающей среды. На рис. 9.8 у нас есть четыре сферы с четырьмя различными настройками свойств материала. Мы используем только шейдер URP/Lit, который поставляется с URP, чтобы протестировать их. В этом разделе мы рассмотрим только две левые сферы. Крайняя левая сфера белая, а параметр металла установлен на 0. Этот материал не принимает цвета окружающей среды. Он принимает только информацию об освещении и его базовом белом цвете. Вторая сфера, хотя она по-прежнему имеет белый цвет в качестве базового, имеет параметр металлик, установленный на 1. Гладкость установлена на 0.5, чтобы быть нейтральной, о чем вы вскоре узнаете больше. Если вы внимательно посмотрите, вторая сфера имеет цвет скайбокса Unity по умолчанию. Теперь нам нужно добавить гладкости этому материалу.
Рис. 9.8. Слева направо: без металла, сплошной металл, весь металл не гладкий, весь металл полностью гладкий
196 Визуальные эффекты Smoothness Продолжая рис. 9.8, мы перейдем к двум правым сферам. Третья слева сфера очень интересна. У нее есть базовый белый цвет, параметр металла установлен на 1, а гладкость установлена на 0. Это означает, что вся сфера полностью рассеяна! Рассеянность в данном случае означает, что все цвета окружающей среды смешиваются по всей сфере, что приводит к почти идеальному истинному нейтральному серому цвету. Четвертая сфера такая же, но гладкость установлена на 1. Это означает, что вся сфера отражает непосредственное окружение. Emissive Чтобы объект излучал свет, вы смотрите на карту излучения (emissive map). Цель такой карты состоит в том, чтобы дать цветам более яркие значения, чем 1. Превышение значения 1 позволяет этой части вашего объекта ярко светиться. Это полезно для лавы, научно-фантастического света или всего, чему вы хотите придать эмиссию. В противном случае Fragment Node по умолчанию имеет черный цвет, и эмиссия не создается.
Рис. 9.9. Слева – отсутствие эмиссии, справа – эмиссия с интенсивностью 2.4, свечение отсутствует
Это не похоже на светящийся гриб! По той причине, что для эмиссии требуется объем постобработки. Для этого создайте пустой GameObject и назовите его _PostProcess. Мы назвали его так, чтобы дать ему совершенно другое имя. Используя подчеркивание в качестве префикса, мы сообщаем нашим разработчикам, что этот объект содержит только логику. В самой игре нет GameObjects. Затем добавьте компонент Volume, показанный ниже на рис. 9.10. Мы еще не закончили! Нужно добавить профиль и переопределение, чтобы настроить свечение. Нажав кнопку New в правой части компонента Volume, вы создадите профиль, в который будут добавлены ваши настройки. Это позволяет вам сохранять эти настройки для других сцен, если нужно. Добавив профиль, сможете добавить переопределение.
Shader Graph 197
Рис. 9.10. Volume, добавленный к GameObject после обработки
Нажмем Add Override, затем Post Process и, наконец, Bloom. Затем установите флажок Intensity в Boolean, чтобы разрешить изменение интенсивности. Измените его значение на 1. Настройки показаны ниже на рис. 9.11.
Рис. 9.11. Переопределение Bloom
Теперь мы видим, как он излучает свет вокруг гриба на экране. Это не добавляет света в сцену; это всего лишь добавление значения яркости вне меша к рендерингу на экране.
198 Визуальные эффекты
Рис. 9.12. Слева: без эмиссии, справа: эмиссия и установка свечения
У нас получился блестящий, светящийся гриб! Теперь попробуйте сделать то же самое на других предметах! Далее рассмотрим Ambient Occlusion. Ambient Occlusion Суть Ambient Occlusion (AO) заключается в добавлении темных пятен к секциям, чтобы показать кризы. Это добавляет хороший, чистый эффект теней, даже если нет никаких конкретных источников света, создающих тени. AO разработан для света под любым углом. Данный атрибут принимает значения от 0 до 1. Если у вас нет карты AO для модели, лучше оставить его равным 1. В главе 12 «Последние штрихи» мы поработаем с материалом Мивари, который будет иметь карту AO. Итак, это был master stack. Каждый из атрибутов в стеке может использоваться для предоставления уникальных шейдеров. Что-то, что помогает с еще большей настройкой, – это часть Shader Graph для Blackboard.
Blackboard Blackboard позволяет создавать свойства и ключевые слова для использования в шейдерах, которые можно динамически изменять различными способами. Свойства можно использовать в шейдере или отображать для материала в инспекторе с помощью флажка Exposed. Вы можете найти кнопку Blackboard в правом верхнем углу окна Shader Graph. После того как вы нажмете эту кнопку, Blackboard откроет отдельный пользовательский интерфейс. Можно создать 16 доступных типов данных. Эти типы данных также можно изменить с помощью скриптов во время выполнения. Ключевые слова предназначены для изменения каждого экземпляра материала во время выполнения. Для этого есть только несколько вариантов, поскольку компилятору необходимо учитывать все варианты. Это удобно для таких вещей, как мобильные специ фикации. Вы можете создать перечисление (определяемый пользователем набор ограничений) или список для разных систем, чтобы изменить точность (качество восприятия) шейдера и адаптироваться к ограничениям платформы.
Shader Graph 199
Рис. 9.13. Типы переменных, доступные в Blackboard
Graph Inspector Graph Inspector предоставляет вам параметры самого типа шейдера. Мы решили начать с шейдера URP/Lit в качестве основы, который задает для нас определенные настройки в Graph Inspector. На рис. 9.14 ниже показан Graph Inspector. Это настройки, установленные по умолчанию в URP/Lit. Каждое из свойств, доступных для создаваемого нами шейдера, очень полезные в определенных ситуациях. Позже мы объясним, почему вносим изменения в настройки. На данный момент поймите, что материал, который мы выбрали, был вариантом Lit, и по умолчанию он представляет собой металлический непрозрачный объект. Это означает, что вы не видите цвет предметов за ним, поскольку он непрозрачен.
Main Preview Main Preview – это общий взгляд на то, как шейдер будет выглядеть в игре. По умолчанию это сфера. Вы можете щелкнуть правой кнопкой мыши в окне, чтобы получить доступ к нескольким параметрам, например включить кастомный меш, как показано на рис. 9.15.
200 Визуальные эффекты
Рис. 9.14. Graph Inspector
Рис. 9.15. Скриншот Main Preview в Shader Graph и его параметров
Пока вы не подключите несколько нод к Master Stack, этот предварительный просмотр по умолчанию будет отображаться в виде серой сферы. Давайте поговорим о нодах дальше!
Nodes Нода – это фрагмент кода, который будет иметь как минимум выходные данные, но также может иметь и входные данные в зависимости от потребностей
Shader Graph 201 ноды. Цель состоит в том, чтобы изменить данные и обеспечить манипулирование вводом атрибутов в главном стеке. Если вы помните, на рис. 9.6 мы показали несколько нод: Color, Lerp, Split и UV. Мы использовали их в комбинации, чтобы изменить базовый цвет, показать созданный нами градиент. На рис. 9.16 показан фрагмент экрана, когда мы нажимали клавишу пробела, пока мышь находилась в пустой серой области Shader Graph. Существует множество нод, которые можно использовать вместе для создания ряда различных эффектов. Теперь вы можете выделить время на поиск в меню нод, которые мы сделали на рис. 9.6, чтобы поиграть с градиентом самостоятельно.
Рис. 9.16. Меню создания ноды
Вам наверняка потребовалось некоторое время. Возможно, также открыли некоторые группы нод и поняли, что существует очень большое количество нод на выбор. Это может вызвать небольшое беспокойство. К счастью, в следующем разделе мы рассмотрим некоторые общие ноды, которые используются во многих шейдерах.
Часто используемые ноды Ниже приведен простой список часто используемых нод для создания шейдеров. Стоит подчеркнуть, что это не полный список. На самом деле сейчас в Shader Graph 10+ более 200 нод. Перечисление всех может буквально занять книгу или две. Интересная особенность этих наборов нод заключается в том, что из них можно создавать невероятные шейдеры. При чтении о них следует помнить, что в предыдущем ноде может быть информация, помогающая описать текущий нод, который вы читаете. Прочтите их все, даже если вам достаточно, например, знать, как добавлять их.
Add Чтобы иметь возможность объяснить ноды Add, мы должны убедиться, что вы помните: 0 означает отсутствие или черный цвет в данном случае. То есть зна-
202 Визуальные эффекты чение 1 соответствует белому цвету. Мы нормализуем наши значения по шкале от 0 до 1 для многих приложений. Возможно, вы помните, что координаты UV также находятся в диапазоне от 0 до 1. Это не ошибка! Итак, если бы у меня было два скаляра или Vector1 и я добавил их, значение было бы больше. Давайте покажем быстрый пример: 0.4 + 0.4 = 0.8.
Рис. 9.17. Добавление ноды
В данном случае 0.4 – это значение темнее средне-серого. Если мы сложим два из них, получим почти белый цвет! 0.8 соответствует значению 80 % чисто белого цвета, как показано на рис. 9.17.
Color Данная нода представляет собой Vector4 с приятным визуальным сахаром сверху. Vector4 (0, 0, 0, 0) представляет красный, зеленый, синий и альфа-канал в значениях шейдера. Color имеет интерфейс для выбора нужного цвета, и он установит для вас значения RGB с помощью ползунка Alpha для этого значения при выводе Vector4.
Рис. 9.18. Нода Color
Lerp Lerp – линейная интерполяция. Ноду Lerp можно использовать во многих приложениях. Одним из примеров является установка градиента для использования в качестве основного цвета, как на рис. 9.6. У нас есть три входа: A, B и T. Представляйте, как будто A – это 0, B – это 1, а T – драйвер. Драйвер (T) имеет значе-
Shader Graph 203 ние 0–1. Однако значение будет сопоставлено со значением A, B и промежуточными значениями. Если T равно 0, будет отображаться 100%-ное значение того, что находится в A. Если T имеет значение 1, оно отобразит 100%-ное значение B. Теперь если T равно 0.4, то это будет между значениями A и B: 40 % A и 60 % B.
Рис. 9.19. Нода Lerp
Это трудно визуализировать с помощью одних только цифр. К счастью, на рис. 9.19 мы использовали UV для ввода T в качестве градиента. Так мы видим, как 0–1 идет снизу вверх. Вы видите градиент от А к Б снизу вверх.
Multiply Мы рассмотрели ноды Add и Lerp. Теперь нужно проработать другую операцию – Multiply (умножение). По характеру основных арифметических операций функция Multiply уменьшает значение. Это происходит потому, что мы находимся в диапазоне 0–1. Давайте составим пример на рис. 9.20.
Рис. 9.20. Нода Multiply
204 Визуальные эффекты Мы использовали похожий пример, что и для ноды Add, только в данном случае – умножение вместо сложения. Простая математика утверждает 0.4 * 0.4 = 0.16.
Sample Texture 2D Данная нода позволяет брать текстуры, созданные вами в другом программном обеспечении для создания цифрового контента (DCC), таком как Photoshop, и использовать информацию о цвете для управления атрибутами Master stack. Есть два входа: текстура, которую вы хотите семплировать, и UV, как показано на рис. 9.21.
Рис. 9.21. Sample Texture 2D
Нода UV позволяет манипулировать UV-координатами вашего меша. Приятной особенностью ноды является то, что она выводит Vector4, а также каждое число с плавающей точкой отдельно от самой ноды.
Saturate Может получиться, что ваши значения превышают значение 1. Это может произойти из-за того, что вы работаете с несколькими нодами, которые выталкивают ваши значения за пределы диапазона 0–1.
Рис. 9.22. Нода Saturate
Если такое произойдет, можете ввести данные в ноду насыщения, что вернет все ваши значения в диапазоне 0–1. Поместите значение с плавающей точкой в часть In, и значение Out будет нормализовано до диапазона 0–1.
Shader Graph 205
Split Как мы видели в ноде Sample Texture 2D, Vector4 был разделен на отдельные выходные данные. Это не всегда так. Нода Color выводит только Vector4. Если вы хотите использовать только значение канала Red из Color, как вы можете его получить? Как вы уже догадались, это нода Split. Введите Vector2, 3 или 4 и используйте любой канал в качестве числа с плавающей точкой.
Рис. 9.23. Нода Split
Лучше всего иметь возможность разбить изображение, на которое вы поместили четыре изображения в градациях серого. Называется это channel packing, с помощью которого можно иметь три изображения при одном поиске текстуры.
UV Не исключено, что когда-нибудь вам нужно будет манипулировать UV объекта, который вы хотите визуализировать. Причина этого может заключаться в том, что вы хотите замостить UV, потому что масштаб элемента был больше или меньше, чем предполагалось. Еще одна причина использования ноды UV заключается в том, что она автоматически создает горизонтальные и вертикальные градиенты.
Рис. 9.24. Нода UV
206 Визуальные эффекты При сплите канал R представляет собой горизонтальный градиент, а канал G – вертикальный.
Векторы Этих ребят используют везде. Вы заметите, что Vector1 называется Float. Другое название Vector1 – Scalar. Еще вы, возможно, заметили, это то, что все выходные данные имеют разные цвета. Float – голубой, Vector2 – зеленый, Vector3 – желтый, а Vector4 – розовый. Очень полезно знать цвета каждого, поскольку они показаны на линиях соединения между нодами. Можно бесконечно перечислять варианты их использования. Вам нужны три точки данных? Vector3!
Рис. 9.25. Ноды Vector
Со всеми этими базовыми нодами вы можете делать мощные шейдеры для создания красивых материалов. В главе 12 «Последние штрихи» мы рассмотрим шейдеры для различных целей и покажем, как использовать эти ноды для создания приятных визуальных конфеток. Теперь давайте отойдем от Shader Graph и поработаем с системами частиц, чтобы мы могли добавить приятные визуальные эффекты к нашему опыту.
Системы частиц Когда вы думаете о визуальных эффектах в видеоиграх, первое, что, скорее всего, приходит вам в голову, – это следы легендарного оружия в ARPG или потрясающие взрывы в напряженных кампаниях шутеров от первого лица. Что бы ни пришло вам в голову, существуют системы, которые позволяют реализовать эти эффекты. Системы частиц позволяют создавать сетки с определенными правилами для создания этих эффектов. Двумя из них в Unity являются Shuriken и VFX Graph.
Shuriken Эта система полна функций, помогающих создавать 3D-меши (структурный набор вершин, ребер и граней, определяющих 3D-объект). Вы можете создавать угли, следы, взрывы, дым и все остальное, чтобы улучшить опыт, который был определен. Как вы можете видеть на рис. 9.26, есть много вариантов, которые можно просмотреть. Мы оставим объяснение этого для примеров, описанных в главе 12, когда создаем эффекты на основе Shuriken. Некоторые знания высокого уровня о Shuriken, которыми необходимо располагать, – то, что это система частиц, использующая ЦП для управления ча-
Системы частиц 207 стицами. Это ограничивает количество частиц, которые могут появляться непосредственно на вашем оборудовании.
Рис. 9.26. Shuriken Particle System
Shuriken отлично подходит для систем частиц на процессоре, но, если хотите, чтобы вокруг двигалось большее количество частиц, вам подойдет VFX Graph. Это создает частицы, управляемые графическим процессором, и может обрабатывать многие тысячи частиц одновременно.
VFX Graph Во-первых, вам, скорее всего, потребуется установить VFX Graph. Откройте Package Manager, как вы это делали раньше, найдите Visual Effects Graph в Unity Registry и установите его! После этого нужно будет создать систему VFX Graph. В окне проекта щелкните правой кнопкой мыши и выберите Create > Visual Effects > Visual Effects Graph.
208 Визуальные эффекты
Рис. 9.27. Установка и создание вашей первой системы VFX Graph
Открытие VFX Graph представит вам новое окно. Это окно напоминает Shader Graph. Вы заметите, что есть Blackboard, его мы можем использовать для создания параметров, которые можно изменить во время выполнения и которые отображаются в инспекторе. Существует уникальный пользовательский интерфейс VFX Graph и несколько конкретных терминов: Contexts, Blocks, Nodes и Variables. Contexts – это части системы, такие как Spawning, Initialization, Update и Output. Внутри каждого из этих контекстов есть блоки, которые можно добавить в контексты, щелкнув правой кнопкой мыши. Spawning context контролирует,
Системы частиц 209 сколько экземпляров системы подается в Initialize context. Initialize context обрабатывает Spawn Event и запускает новую симуляцию частиц. Update context принимает инициализированные частицы и выполняет явное поведение при определенных условиях. Output contexts учитывают данные симуляции и отображают каждую живую частицу в соответствии с конфигурацией Output context. Однако Output context не изменяет данные симуляции. Первый контекст – спавн, позволяющий добавлять блоки, чтобы влиять на логику создания каждой системы. Здесь следует рассмотреть несколько вопросов: сколько частиц должно спавниться из этой системы? Как быстро должны спавниться эти частицы? Когда они спавнятся?
Рис. 9.28. Spawn context
Далее вам нужно иметь параметры для их инициализации. Эти блоки отвечают на следующие типы вопросов: «Где спавнятся частицы?», «Они спавнятся в движении или у них есть ускорение?», «Сколько времени живет каждая частица?»
Рис. 9.29. Initialize context
210 Визуальные эффекты Теперь, когда у вас есть их спавн, может быть хорошей идеей добавить им какое-то уникальное поведение по мере их обновления, или же они будут плавающими сферическими градиентами. Вы можете ожидать, что это будет ответом на главный вопрос: как частицы будут меняться со временем?
Рис. 9.30. Update context
Наконец, когда у вас есть понимание того, сколько существует частиц, где они находятся и что они делают, вы теперь можете решить, как они будут выглядеть. Вопросы таковы: «В какую сторону обращены частицы?», «Какой шейдер используется?»
Рис. 9.31. Output context
Возможно, вы заметили, что левая сторона блоков иногда имеет круглые входные данные, такие как Shader Graph. Если вы думали, что можете внести
Заключение 211 в это какой-то вклад, возможно, от нод, то вы правы! Есть ноды, через которые вы можете получить правильные данные, поступающие к блокам, находящимся в каждом контексте.
Nodes Как и в Shader Graph, ноды должны принимать значения данных и манипулировать ими таким образом, чтобы получить желаемый результат. В случае VFX Graph значения предназначены для чтения в один из блоков, а не в один из атрибутов из Master Stack. По большей части вы будете использовать ноды Operator и переменные, созданные в Blackboard, для выполнения сложных математических операций.
Заключение Мы узнали, что визуальные эффекты имеют серьезный технический подтекст благодаря двум основным источникам в Unity: шейдерам и частицам. Не торопясь с шейдерами, мы построили пример материала на 3D-объекте для понимания концепции, чтобы, когда у нас есть несколько разных скриптов, можно было проследить, как был создан шейдер. Это было сделано с помощью Shader Graph. Далее мы погрузились в концепцию частиц. Было показано, что Shuriken дает простое представление о частицах ЦП и будет использоваться в последующих главах для объяснения последних штрихов. Частицы графического процессора создаются с помощью VFX Graph; мы рассмотрели интерфейс и некоторый словарный запас VFX Graph, поэтому, когда мы будем использовать его позже, есть понимание, которое осталось отработать. Визуальные эффекты – очень большая тема для освоения. Овладение данными инструментами занимает много времени. Не торопитесь, работая над этим, и натыкайтесь на ошибки. Это поможет вам понять визуальные эффекты быстрее. Следующая глава посвящена реализации звука в игре. На звуки обычно не обращают внимания почти на протяжении всей игры, но они являются неотъемлемой частью обеспечения захватывающей эмоциональной связи продукта с окружающей средой и персонажем. В следующей главе мы рассмотрим реализации, некоторый звуковой дизайн и другие вопросы, связанные со звуком.
10 Звуковые эффекты Звуковые эффекты! Звук – это единственная часть видеоигры, которая исходит из реального мира и переносится в игру. Используя микрофоны, звукорежиссеры записывают распространенные звуки видеоигр, такие как голос за кадром, музыка, звуки пользовательского интерфейса, оружия и окружающие звуки, чтобы оживить игру! Звук, как правило, оказывает очень тонкое влияние на то, как игроки воспринимают качество видеоигры. Красивая анимация в игре может быть хороша, только если хорош ее звук. В этой главе мы рассмотрим пять элементов выбора или проектирования звуков. Это источник, огибающая, высота тона, частота и наслоение. Понимание этих пяти элементов даст вам прочную основу для того, чтобы ваши звуки соответствовали общему замыслу повествования, персонажа, окружения и механики, над которыми мы работали до сих пор. Затем мы рассмотрим, как масштабировать эти элементы в игровом движке с помощью нашего кода и микширования! Звуки рассказывают историю на основе отдельных звуковых эффектов, а также работают вместе, чтобы рассказать большую, более глубокую историю. Наконец, рассмотрим конкретные примеры звукового дизайна в нашем проекте, а также их реализацию в Unity. Эти примеры включают магические звуки, звуки шагов и окружающие звуки. Вот краткий обзор данной главы: пять элементов звукового дизайна, проектирование в большом масштабе, звуковой дизайн и реализация нашего проекта, запуск звука через взаимодействие с игроком.
Звуковой... дизайн? Звуковой дизайн! Забытый приемный ребенок видеоигр, но также и душа, и эмоции, стоящие за ними. Простое объяснение звукового дизайна заключается в том, что звук записывается, обрабатывается, а затем кодируется непосредственно в игре. Таким образом, это делает звук единственной частью видеоигры, которая исходит непосредственно из реального мира. Любой звук, на который мы ссылаемся в этой главе, можно найти в папке / Assets/Sounds/[Name].
Пять элементов звукового дизайна 213
Пять элементов звукового дизайна Элементы звукового дизайна, которые мы будем обсуждать, – это источник (source), огибающая (envelope), высота тона (pitch), частота (frequency) и наслоение (layering). Они применяются к процессу создания отдельного звукового эффекта, а также к более широкому спектру того, как звуки работают вместе в игре.
Источник Источником может быть человек, место или вещь, из которых исходит или черпается ваше вдохновение. Ваш источник – это то, что помогает слушателю понять реальные характеристики звука. Если вы запишете звук шагов, ударяющихся по травянистой поверхности, а не по бетону, мельчайшие качества и различия между этими двумя звуками помогут нам различить их. И таким образом, мы можем использовать источник как творческое ограничение в создании аутентичных звуков. Ограничения – это то, что художники используют, чтобы избавиться от беспорядка в нашем мозгу и создать свое видение. Итак, в процессе записи, если нам нужен волшебный звук воды в видеоигре, мы записываем немного звуков воды в качестве базового слоя. Или, если мы хотим записать звук для анимации собаки, катающейся по грязи, первое и лучшее, что нужно записать, – это звук собаки, катающейся по грязи. То, для чего мы создаем звуки, помогает нам выбирать, что записывать! Запись звуков может быть сложным процессом, за которым стоит целое искусство; хотя это не совсем поможет вашему росту как звукового дизайнера, я настоятельно рекомендую на данном этапе использовать существующие звуковые библиотеки. Почти любой звук, который вы можете придумать, уже записан, поэтому имеет смысл просто купить или скачать звуки онлайн! Это поможет значительно ускорить рабочий процесс. Если вы не хотите использовать звуковые библиотеки, можете взять микрофон! Использование микрофона – очень глубокий процесс, который мы не будем описывать здесь, потому что об этом искусстве можно написать целые отдельные книги. Вот несколько популярных бесплатных веб-сайтов и звуковых библиотек: Blipsounds: https://blipsounds.com/community-library/; Andrew V Scott: https://www.andrewvscott.com/Building-ALarge-SFX-Library-for-Free; SKYES Audio: https://www.skyesaudio.com/blog/2019/4/1/ the-ultimate-free-sound-effects-list-free-to-download; Freesound: https://freesound.org/. Есть много других, которые можно найти в интернете. Не бойтесь искать, чтобы найти именно то, что соответствует вашим потребностям.
214 Звуковые эффекты В видеоиграх источник звукового эффекта обычно определяется тем, что вы видите визуально. Для ледяного мага, использующего заклинание ледяного ветра, для начала вы ограничитесь использованием звуков ветра и льда. Если у вас есть пистолет из желе, который стреляет в крошечных лошадей силой радиоактивного пукания, вы, вероятно, будете использовать звуки желе, лошадей, пукания и выстрелов. Итак, если мы возьмем волшебный меч с металлическими текстурами и фиолетовые магические визуальные эффекты, которые движутся вдоль лезвия, какие звуки мы станем искать? Вы, наверное, уже догадались – будем использовать звуки из волшебной звуковой библиотеки и несколько металлических звенящих звуков. Другой способ определить ваш источник – через контекст истории. Тот же самый волшебный меч может выглядеть как волшебный, но, возможно, авторы игры определили, что клинок использует футуристическую магию, поэтому вам нужно использовать научно-фантастические звуковые элементы. Следует отметить, что во многих играх есть ограничения, в которых звук должен заполнять пробелы. Игра, эмулирующая графику Atari 2600 с реалистичным звуковым оформлением, может потребовать некоторого воображения. Зеленая зона, по которой ходит ваш игрок, может быть травой или свалкой токсичных отходов в зависимости от контекста игрового мира.
Огибающие Огибающая – это способ для звуковых дизайнеров объяснить громкость звука во времени (громкость в децибелах, а не 3D-модели). Две части огибающих, которые вы будете использовать, – это атака и затухание. Атака, показанная ниже на рис. 10.1, – это начало звука, а затухание – конец. Мы описываем атаку и затухание звука скоростью (быстро и медленно).
Продолжительность
Атака
Распад
Затухание
Рис. 10.1. Пример огибающей с пояснением
Атака Примером звука при медленной атаке может быть что-то вроде звукового эффекта «вжух», когда меч пронзает воздух. Звук начинается почти неслышимым и нарастает в течение полсекунды. Мы можем сделать атаку еще медленнее,
Пять элементов звукового дизайна 215 увеличивая громкость в течение нескольких секунд. На рис. 10.2 вы можете непосредственно увидеть, как огибающая будет выглядеть на временной волне. Звуки с более медленной атакой кажутся игроку более тонкими и нежными. Некоторыми другими примерами звуков с медленной атакой могут быть проезжающая мимо машина или чайник, готовящийся свистнуть.
Рис. 10.2. Звук быстрой атаки
Примером звука с быстрой атакой, как на рис. 10.2, может быть звуковой эффект удара кулаком. Как только звук начнется, он будет почти на максимальной громкости, создавая импульсный звук. Импульсный звук – это звук с более быстрой атакой, который кажется игроку более агрессивным, обычно для создания своего рода воздействия, чтобы передать силу или шокировать игрока. Некоторыми другими примерами звука с быстрой атакой могут быть стрельба из оружия, грохот тарелок, как показано на рис. 10.3, или удар молота по наковальне.
Рис. 10.3. Пример быстрой атаки ударом по барабанной тарелке
Затухание Затем у нас есть затухание звука. Как вы, наверное, догадались, мы будем использовать скорость, чтобы определить характер затухания. Примером звука с более медленным высвобождением может быть выключение двигателя автомобиля или взрыв. Большинство звуковых эффектов будут воспроизводиться медленнее, так как это звучит более привлекательно. На самом деле не так много примеров короткого затухания, которые вы услышите в видеоиграх. Жесткая отсечка в звуковом эффекте в большинстве случаев неестественна и неприятна для слуха, за исключением некоторых продвинутых стилизованных приемов. Звук с медленным затуханием может быть громким звоном гонга или шумом удаляющейся машины, когда вы слышите, как громкость звука рассеивается. Для примера вот как выглядит медленное затухание:
Рис. 10.4. Звук медленного затухания
216 Звуковые эффекты А вот как выглядит быстрое затухание:
Рис. 10.5. Звук быстрого затухания
Еще одним элементом звука является высота тона.
Высота тона Высота тона – это элемент звука, определяющий его «высокость» или «низкость». Это, вероятно, одна из самых простых концепций для понимания, поскольку мы слышим ее в фильмах, видеоиграх и даже в повседневной жизни. У крупного яркого персонажа в анимационном фильме обычно будет низкий голос, а у маленького милого персонажа может быть высокий. Пример, приведенный выше, является одной из наиболее распространенных причин для управления высотой звукового эффекта – размером. Другой – скоростью. Подумайте о машине, которая передвигается медленно, и о машине, едущей быстро. Та, что быстрее, имеет более высокие обороты двигателя, в то время как неработающий или медленно движущийся автомобиль будет резонировать низкочастотными звуками. Чтобы полностью понять высоту тона, следует разобрать частоту, которая напрямую с ней связана.
Частота Частота – самый сложный элемент для объяснения, но один из самых важных для понимания. Вы наверняка слушали музыку в машине или на стереосистеме и видели возможность управления «басами» или «высотами». Высокие частоты относятся к более высоким частотам, а басы относятся к более низким частотам. Диапазон человеческого слуха составляет от 20 до 20 000 Гц (Герц), и многие звуки, независимо от того, высокие они или низкие, звучат на каждой частоте. Когда вы воспроизводите звук в своей машине и приглушаете «басы», вы приглушаете низкие частоты. Лучший пример – белый шум. Белый шум – это просто звук, который воспроизводится на каждой частоте с одинаковой громкостью. Если вы думаете, что никогда не слышали белый шум, вспомните, как звучат телевизионные помехи. Вы можете прослушать звук в /Assets/Sounds/WhiteNoise.wav. Найдите его в проекте на GitHub, ссылку на который можно найти в предисловии к этой книге. Самое странное в данном звуке то, что, просто слушая, кажется, звук состоит в основном из более высоких частот. Но мы можем использовать инструмент под названием эквалайзер (или сокращенно EQ), чтобы визуализировать воспроизводимые частоты, а также управлять громкостью отдельных частот.
Пять элементов звукового дизайна 217 Как правило, более высокие частоты воспринимаются как более громкие, что важно учитывать при создании звуков для вашей игры. Если вы хотите, чтобы звук выделялся, включение более высоких частот немного поможет, а их удаление может помочь смешать звуки с фоном. А если нужно, чтобы каждый звук выделялся, но также чтобы он имел вес и силу, нам придется использовать более низкие частоты. Итак, баланс должен быть установлен. Желтые линии на этом графике показывают, где падают частоты, и вы можете видеть, что громкость практически одинакова по всему спектру. Это означает, что каждая отдельная частота обычно имеет одинаковую громкость частоты.
Рис. 10.6. Пример громкости с одинаковой частотой
Я предоставил несколько звуков ниже, чтобы вы все могли услышать разницу, когда мы удаляем более низкие и высокие частоты, а также график, показывающий, какие частоты мы удалили. Когда вы будете слушать, вы услышите и увидите, будто каждый звук в определенной степени охватывает более высокие и низкие частоты, и мы можем управлять этими частотами, чтобы вызывать уникальные ощущения от каждого звука. Послушайте Assets/Sounds/Explosion.wav, а затем Assets/Sounds/ExplosionLP.wav, чтобы услышать, каково это – обрезать высокие частоты. А затем послушайте ExplosionHP.wav, чтобы узнать то же самое про низкие частоты. Послушайте Assets/Sounds/BeamSword.wav, а затем Assets/Sounds/BeamSwordLP.wav, чтобы узнать, каково это – обрезать высокие частоты. А затем послушайте BeamSwordHP.wav, где вырезаются низкие частоты. Послушайте Assets/Sounds/MagicIceSpell.wav и Assets/Sounds/MagicIceSpellLP. wav, чтобы узнать, как обрезать высокие частоты; а затем – MagicIceSpellHP.wav, чтобы услышать, как обрезаются низкие частоты. Assets/Sounds/FootstepHP.wav – чтобы услышать вырезанные низкие частоты. Прослушайте Assets/Sounds/MagicIceSpell.wav, а затем Assets/Sounds/ MagicIceSpellLP.wav, чтобы узнать, каково это – обрезать высокие частоты. MagicIceSpellHP.wav – чтобы услышать, как обрезаются низкие частоты. А затем послушайте Assets/Sounds/FootstepHP.wav, где вырезаются низкие частоты.
218 Звуковые эффекты Причина, по которой частота является одной из самых сложных концепций для понимания, заключается в том, что ваши уши не обучены ее слушать. Мы просто слышим звук и знаем, хорошо он звучит или нет.
Наслоение Наслоение – это самая простая для понимания концепция пяти элементов. В то время как визуальные среды почти все мультипликативны, звуковые среды являются аддитивными. Просто говоря, это процесс одновременного воспроизведения нескольких звуков в уникальной последовательности. Далее у нас есть четыре уникальных звуковых эффекта, которые находятся раздельно. Это «удар», «пик», «завершение» и «бас». Если слушать каждый по отдельности, они кажутся пустыми, но, как только мы сложим их все вместе, у нас получится прекрасный звук взрыва. Наслоение – полезный процесс, потому что мы можем взять такие источники, как «Научно-фантастическая энергия» и «Металлический меч», и объединить их вместе, чтобы создать «Научно-фантастический энергетический меч». Или мы можем взять нашу пушку и выбрать слои на основе описания и функциональности пушки. Прослушайте Assets/Sounds/ScifiEnergySword01.wav, Assets/Sounds/ScifiEnergySword02.wav, Assets/Sounds/ScifiEnergySword03.wav. Наслоение также позволяет нам разбивать частоты на более изолированные части. Мы можем взять звук, который в основном состоит из более низких частот, и добавить его к обычному звуку, чтобы придать ему больше жирности и мощности. Послушайте Assets/Sounds/Splash01.wav, затем – более низкие частоты, добавленные в Assets/Sounds/Splash02.wav, чтобы сравнить, как они становятся более мощными. Мы также можем наслоить два звука с разными огибающими, один с длинной атакой, а другой – с быстрой, чтобы создать крутой эффект нарастания и добавить воздействия. Прослушайте Assets/Sounds/EarthSpell01.wav, чтобы услышать первоначальный звук, а затем прослушайте Assets/Sounds/EarthSpell02. wav после наслоения и обратите внимание, как мы можем изменить историю нашего звука! Теперь, когда мы рассмотрели, из чего состоит звук и как он применяется к одному создаваемому звуковому эффекту, разберем применение этих пяти элементов в более широком масштабе игры.
Проектирование в большом масштабе В отличие от искусства, которое мультипликативно, создание звуковых эффектов – это полностью аддитивный процесс. Например, если у нас есть 100 звуков, неважно, какова их громкость или частотный диапазон, это может в конечном итоге создать много беспорядка. В пространстве видеоигры мы должны быть готовы к тому, что любые звуки, будь то взмах меча, окружающий звук, оркестровая партитура или закадровый голос, будут воспроизводиться одновременно. У нас есть инструменты для индивидуального управления этими звуками, но мы должны убедиться, что у нас есть то, что называется сбалансированным миксом.
Реализация звукового дизайна нашего проекта 219
С какой стороны подойти к созданию звуков для игры Итак, куда же деваются звуки в вашей игре? Звуки часто можно упускать из виду, поскольку технически они не нужны для создания того, что определяется как «игра». И из-за этого сложно сразу подумать о том, что должно звучать, а что нет. Проще говоря, мне нравится искать в игре все, что движется. Вплоть до мельчайших тонкостей. Добавление звуков для NPC может включать в себя дыхание, звук их ног, ударяющихся о поверхность, звук шуршания их одежды... Все эти звуки можно использовать в игре. Позже в главе 11 мы поговорим о некоторых причинах того, почему может быть трудно добиться столь пристального внимания к деталям. Иногда искусство в игре минимально. То, что вы видите глазами, не всегда дает достаточно информации, поэтому используйте свое воображение, чтобы понять, какие звуки можно добавить! Чем больше вы добавите к звуку, тем лучше. Иногда игра может быть не чем иным, как пиксельной графикой, и у вас может быть стимул добавлять меньше звуков, но нужно всегда думать о том, какое внимание к деталям вы можете услышать, а не увидеть! Если игрок падает в яму, где вы не можете видеть, создание звукового опыта может рассказать более подробную историю, чем визуальные эффекты! Может, там шипы, бездонная яма или лава! Мы бы хотели, чтобы игрок слышал пузырьки лавы, удар шипов, пронзающих игрока, или свист персонажа, падающего с крутого утеса!
Реализация звукового дизайна нашего проекта Мы считаем, что лучший способ учиться – это сразу же начать понимать, как все работает. Этот процесс будет относительно прост теперь, когда мы рассмотрели непростой движок Unity.
Получение нашего первого звука для воспроизведения Для начала давайте добавим в наш проект несколько аудиофайлов. Начнем с создания папки в Unity под названием Audio, а затем перетащим в нее наш файл Assets/Sounds/TestTone.wav. Теперь, когда у нас есть звук в папке, давайте создадим пустой GameObject в сцене прямо рядом с нашим персонажем. А пока назовем этот GameObject Sound Emitter. В нынешнем виде этот GameObject ничего не делает. Итак, давайте щелкнем и перетащим звук из папки Unity прямо в инспектор нашего игрового объекта Sound Emitter. Так в GameObject автоматически создастся компонент Audio Source. Это то, что позволяет нашим звуковым эффектам воспроизводиться в Unity! Давайте продолжим и нажмем Рlay и посмотрим, что же произойдет! Если вы следовали правильно, вы, вероятно, уже услышали свой первый звук! Поздравляю! Это, конечно, просто звук-заполнитель, поэтому мы больше сосредоточимся на добавлении других звуков, необходимых в игре. В этом процессе мы рассмотрим параметры, которые можем изменить в компоненте Audio Source.
220 Звуковые эффекты
Организация проекта Чтобы лучше организовать данный проект, давайте добавим новый префаб с именем ====SFX====. Сюда мы поместим все звуковые эффекты, которые существуют в нашей сцене. В дополнение к этому создадим две новые папки в /Assets/Sounds/ в нашем проекте GitHub. У нас будет папка с sources и папка с prefabs.
Музыка Музыка – важная часть видеоигр. Звуковые эффекты помогают оживить игру благодаря подробным окружающим звукам, звукам персонажей и выразительному голосу за кадром, но именно музыка – это то, что помогает вызывать у игрока ежеминутные эмоции. У вас могут возникнуть вопросы о разнице между звуками и музыкой. Технически они одинаковы, но, чтобы упростить общение о них, большинство профессиональных звукорежиссеров считают музыку партитурой или такими инструментами, как фортепиано, скрипки, гитары и барабаны, работающими вместе для создания цельной песни или музыкального трека для прослушивания в качестве фоновой музыки. Между тем звуковые эффекты обычно представляют собой реальные экземпляры, такие как звуки шагов, мечей, пользовательского интерфейса и т. д. Давайте же добавим музыку в нашу игру! Чтобы сделать это, нужно взять GameObject SFX в нашей сцене и переименовать его в Music. Давайте послушаем наши Assets/Sounds/Music_01.wav. Сначала выберите аудио-файл в своей папке и нажмите кнопку зацикливания в нижней части инспектора. Это кнопка прямо над сигналом в крайнем правом углу, показанная на рис. 10.7 ниже.
Рис. 10.7. Wave play UI в инспекторе
Теперь нажмите кнопку Play, которая представляет собой боковой треугольник справа в инспекторе, но слева от кнопки зацикливания. Если вы прослушаете музыку до конца, то поймете, что она представляет собой бесшовную петлю! Чтобы услышать это в действии в игре, давайте возьмем наш игровой объект Sound Emitter в сцену и переименуем его в Music. Затем щелкнем и перетащим наш Music в новый GameObject Music. Поместим его в пространство AudioClip компонента Audio Source, показанного на рис. 10.8. Если бы мы протестировали игру прямо сейчас, мы бы услышали звук, но в конце концов он прекратился бы. Но мы нажали кнопку зацикливания в инспекторе для файла .wav, верно? Так почему же это не работает? Что ж, эта кнопка зацикливания была предназначена исключительно для воспроизведения в том одном случае. Если мы хотим зациклить звук в компоненте Audio Source, то следует включить Loop, как показано в разделе Play On Awake на рис. 10.8. Теперь, если мы сыграем в игру, наша музыка будет зацикливаться! Как здорово!
Реализация звукового дизайна нашего проекта 221
Рис. 10.8. Компонент Audio Source
Позже в этой главе отрегулируем громкость всех звуков в игре, что называется «микшированием» или «микшированием игры». Мы не собираемся микшировать игру сразу, потому что громкость каждого звука в игре полностью зависит от того, как он звучит по отношению к другим звуковым эффектам!
2D-звуки До сих пор мы слышали только 2D-звуковые эффекты. 2D-звуковые эффекты – это звуки, которые не имеют внутриигрового местоположения и будут воспроизводиться в любом месте для игрока. Независимо от того, где вы перемещаетесь по карте, звук 2D будет воспроизводиться как постоянный триггер. Ниже приведен список 2D-звуков, которые вы, вероятно, слышали, играя в видеоигры. Музыка: вступительная музыка, которая воспроизводится во время открытия меню, прежде чем вы нажмете кнопку воспроизведения. Пользовательский интерфейс: нажатие кнопки, слышно «щелчок» кнопки. Голос за кадром: рассказчик говорит во время игры. Звук окружения: общий звук, воспроизводимый в определенном месте, например шум ветра. Все вышеперечисленные категории могут быть вызваны действием игрока, игровым событием, простым запуском игры или входом в новую область игрового мира. Но не все они будут существовать в 3D-пространстве игры. Это делает их 2D-звуковыми эффектами. Итак, теперь, когда мы рассмотрели, что такое 2D-звуки, давайте поговорим о 3D-звуках.
222 Звуковые эффекты
3D-звуки В отличие от 2D-звуков 3D-звуки существуют непосредственно в мире игры. Когда вы играете в игру, несложно сказать, какие звуки являются трехмерными, пока вы перемещаетесь по миру и слушаете, какие звуки возникают в каком ухе. Это называется панорамирование. Панорамирование – это стереофоническое качество звука. Вы когда-нибудь слушали песню в наушниках и снимали их с одного уха только для того, чтобы услышать игру некоторых инструментов вместо всей песни? Вот что такое панорамирование! Музыкальные продюсеры, создавшие эту песню, специально вставили какие-то инструменты в одно ухо, чтобы создать лучший «стерео образ» (обещаю, мы не будем углубляться в это). Таким образом, в реальном мире, если кто-то говорит слева от вас, вы услышите его левым ухом, в то время как правым ухом вы его услышите не так сильно. Мы хотим воссоздать это ощущение в видеоиграх. Итак, определяем позиционный звук как 3D-звук.
Использование 3D-звуков Проведем небольшой эксперимент. Давайте возьмем наш игровой объект Music и изменим параметр Spatial Blend с 0 на 1. Теперь у нас есть пространственное аудио! Это означает, что наши звуки будут в 3D! В нынешнем виде может быть сложно точно определить, откуда играет музыка, потому что для этого нет визуального индикатора. Итак, чтобы исправить это, я создаю сферу GameObject в качестве дочернего элемента нашего аудиоисточника, чтобы точно визуализировать, где он находится!
Рис. 10.9. GameObject Sphere как дочерний объект
Теперь, когда мы нажимаем кнопку воспроизведения, можем точно видеть, откуда воспроизводится источник звука! Далее мы поговорим о том, как управлять параметрами наших звуковых 3D-эффектов.
Аудиослушатель, часть I Как можно слышать звуки в игре? Мы слышим их через наш аудиослушатель. Это компонент, действующий как виртуальная пара ушей, который мы можем
Реализация звукового дизайна нашего проекта 223 разместить на любом игровом объекте. Разместить этот слушатель в большинстве случаев очень просто, но иногда требуется более сложная интеграция. В игре, где используется камера от первого лица, все просто: мы просто добавляем слушателя к камере GameObject, и на этом, пожалуй, все. Мы используем слушатель звука на камере, потому что он действует как уши игрока. Но иногда камера может быть в изометрическом виде и находиться слишком далеко от игрока. Чтобы правильно панорамировать и слышать звуки, когда они перемещаются в мире, мы смещаем прослушиватель звука на новом игровом объекте, чтобы он был смещен в соответствии с камерой. Мы вернемся к этому в разделе «Аудиослушатель, часть II». А пока давайте настроим некоторые настройки 3D-звука. Мы не сможем использовать прослушиватель аудио в практическом смысле, пока не настроим наши 3D-звуки.
Настройки 3D-звука Когда вы слышите звук в реальной жизни, то обычно можете приблизиться к нему, и он станет громче, а когда вы отдалитесь от него, он станет тише и в конце концов затухнет. Мы можем управлять этим эффектом, используя настройки 3D-звука в компоненте Audio Source. Давайте попробуем настройки 3D-звука на минимальном (Min Distance) и максимальном расстоянии (Max Distance). Измените Max Distance на 10 в компоненте Audio Source и нажмите Play. Предполагая, что у вас все еще есть сфера на вашем GameObject, подойдите ближе и дальше от него в игре. Чтобы лучше визуализировать это, давайте открепим нашу вкладку сцены в Unity и поместим ее рядом с вкладкой Game. Теперь мы можем визуализировать минимальное и максимальное расстояние с помощью каркасной гизмо-сферы в игре! Заметьте, когда мы перемещаем нашего игрока за пределы сферы, звук больше не слышим. С помощью ползунка Max Distance контролируется расстояние, на котором мы можем слышать звук. А с помощью Min Distance мы можем контролировать, в какой точке звук будет самым громким. Давайте продолжим и изменим минимальное расстояние на 3. Вы заметите, что меньшая сфера внутри большей сферы изменится, как показано далее в главе на рис. 10.17. Когда мы переместим нашего персонажа внутрь этой сферы, вы заметите, что панорамирование отсутствует. Это потому, что звук достиг максимальной громкости, и внутри меньшей сферы он превратится в 2D! Наконец, мы просто установим Volume Rolloff на Linear Rolloff вместо Logarithmic. Причина, по которой мы делаем это, заключается в том, что, когда вы изменяете максимальное расстояние на число меньше 500 в логарифмическом режиме, звук на самом деле не обрывается на этом расстоянии. Таким образом, если бы мы установили максимальное расстояние равным 10, то продолжали бы слышать его, даже если бы мы находились в 400 единицах по карте и даже если бы установили максимальное расстояние намного меньше этого. Для справки, вот Logarithmic:
224 Звуковые эффекты
Рис. 10.10. Logarithmic Rolloff
А вот Linear:
Рис. 10.11. Linear Rolloff
Аудиослушатель, часть II Вы, возможно, заметили, что звук немного сбивается, когда персонаж находится внутри сферы. Обычно, когда персонаж проходит мимо сферы, звук не
Реализация звукового дизайна нашего проекта 225 самый громкий; он достигает максимальной громкости только тогда, когда камера находится рядом со сферой. Это связано с тем, что слушатель звука Unity по умолчанию настроен на камеру. В игре от третьего лица, подобной той, которую мы делаем, нужно бы добавить камеру к персонажу, но есть одна загвоздка. Надо сделать так, чтобы она была на персонаже, но не вращалась вместе с вращением персонажа. Мы хотим вращать персонажа камерой, как показано на рис. 10.12 ниже.
Рис. 10.12. Выбор камеры в иерархии
Если мы откроем сцену, то увидим, что к MyvariWithCameraRig подключена Main Camera. А внутри инспектора мы найдем компонент Audio Listener, показанный на рис. 10.13. Теперь в качестве эксперимента давайте удалим здесь Audio Listener и переместим его прямо на нашего главного героя. Простое размещение его на игровом объекте Character будет работать нормально. Сейчас поиграйте в игру, двигайтесь от нашего объекта-сферы и к нему, вращайтесь вокруг него. Вы заметите, что панорамирование повсюду! С нашей точки зрения, наблюдая за персонажем, трудно сказать, откуда поступает информация, потому что мы не на месте нашего персонажа; у нас вид от третьего лица. В такой игре мы, вероятно, могли бы просто поместить наш аудиослушатель на камеру, но все же лучше, чтобы он был на модели нашего персонажа. Однако мы не можем этого сделать, потому что игрок не заблокирован своим вращением.
226 Звуковые эффекты
Рис. 10.13 Аудиопрослушиватель на основной камере в инспекторе
Но есть решение! В большинстве игр нам пришлось бы добавлять слушатель в качестве дочернего игрового объекта к основной камере внутри игрового объекта MyvariWithCameraRig. Но здесь мы уже сделали большую часть работы, потому что корневая трансформация MyvariWithCameraRig уже выровнена с моделью персонажа! Все, что нам нужно сделать, – это создать новый GameObject внутри корня MyvariWithCameraRig, переименовать его в Listener, как показано на рис. 10.14, и добавить к нему наш компонент Audio Listener.
Рис. 10.14. Новый GameObject для размещения Audio Listener
Затем мы можем взять этот игровой объект Listener и переместить его вверх по оси Y так, чтобы он выровнялся прямо рядом с ушами персонажа, что видно с помощью гизмо-преобразования ниже на рис. 10.15. Я переместил его на 1.5 единицы вверх по оси Y. Теперь, когда мы перемещаемся, transform игрового объекта Listener будет перемещаться вместе с камерой. Наши 3D-звуки теперь будут воспроизводиться относительно положения персонажа, а не камеры!
Реализация звукового дизайна нашего проекта 227
Рис. 10.15. Игровой объект Audio Listener на Мивари, выровненный по высоте головы
3D-звуки окружающего мира в игре Как часто в своей жизни вы испытываете абсолютную тишину? Можете подумать, что наслаждаться тихим вечером в своей гостиной – это абсолютная тишина, но вы все равно можете слышать работу кондиционера, работающего холодильника, звуки за окном и т. д. Эти упомянутые звуки, конечно, очень тихие, но суть в том, что мы никогда не ощущаем абсолютной тишины! Так что даже в наших видеоиграх, если персонаж бездействует, не двигается и стоит совершенно неподвижно, очень важно всегда иметь какой-то звук. Здесь появляются окружающие звуки. Окружающие звуки обычно можно определить как «звуки, которые существуют в трехмерном пространстве, но не перемещаются». В нашем проекте для Священного леса мы можем добавить звук шелеста деревьев, внутренней части пещеры, гудения портала, магической энергии, исходящей от объекта, рек и многого другого! Добавить окружающий звук довольно просто. Технически мы это уже сделали! Звук, который мы слушали в разделе настроек 3D-звука, технически можно рассматривать как окружающий звук. Давайте начнем с очень простого окружающего звука шелеста деревьев в нашей сцене. Возьмем наш файл Assets/Sounds/AMB_Trees3D.wav и поместим его в компонент источника звука на GameObject. Давайте установим Volume Rolloff на Linear Rolloff и Spatial Blend на 1. Далее, сохраним наше минимальное расстояние равным 1 и максимальное расстояние равным 5. Сделав это, мы можем разместить значения преобразования нашего GameObject, как показано на следующем изображении. Преобразование от-
228 Звуковые эффекты ражает окружающий звук GameObject, показанный на рис. 10.16, и физически доступно в сцене в разделе Sound в иерархии для первого AMB_Trees3DGameObject.
Рис. 10.16. Звуковое преобразование Ambient Tree 3D
Поместим его на большое дерево слева от того места, где появляется персонаж. На следующем изображении вы можете увидеть наш звуковой гизмо, размещенный в сцене. Вы можете увидеть это на рис. 10.17 ниже, а также в сцене. Двойной щелчок по GameObject AMB_Trees3D в иерархии физически перенесет вас в это место на сцене.
Рис. 10.17. Гизмо для источника окружающего звука
Наконец, мы просто убедимся, что установлен флажок Play On Awake, чтобы звук воспроизводился сразу же при запуске сцены, как показано ниже на рис. 10.18.
Рис. 10.18. Play On Awake установлен на True
Запуск звука через взаимодействие с персонажем 229 Теперь давайте нажмем Play. И здесь мы увидим, что звук в игре воспроизводится правильно! Он будет работать точно так же, как и наш предыдущий звук, когда мы можем слышать его направленно, и в конечном итоге звук отключится, когда мы покинем радиус дерева!
Заполнение окружающими звуками Для остальных окружающих звуков это будет повторение того, что мы только что сделали. Мы включим минимальные и максимальные диапазоны и позиции наших окружающих звуков, а также аудиофайлы, которые, по нашему мнению, подходят для каждого элемента окружающего мира. В сцене у нас есть окружающие звуки, настроенные под ====AMB====, как показано на рис. 10.19. Я настоятельно рекомендую вам прислушаться к окружающим звукам и понять, как они звучат для вас!
Рис. 10.19. Список окружающих звуков в иерархии
2D-атмосфера Если вы прогуляетесь по нашей недавно заполненной сцене, то заметите, что теперь она кажется намного живее! Однако также заметите, что в некоторых местах отсутствует какой-либо звук, а абсолютная тишина – это не то, что мы хотим, чтобы наши игроки испытали в игре! Давайте добавим источник звука к нашему родительскому игровому объекту ====SOUND==== и закинем в General2D_Amb.wav.
Запуск звука через взаимодействие с персонажем Все звуки, что мы создали до сих пор, – это звуки, которые будут воспроизводиться, как только вы войдете в сцену. Это происходит из-за того, что установлен флажок Play On Awake в компоненте Audio Source. Если мы не сделаем это, звуки никогда не будут воспроизводиться. Но что здорово, так это то, что мы можем запускать звуки другими способами!
230 Звуковые эффекты
Запуск звука через события Unity Давайте сделаем звук для нашей первой головоломки с лестницей. Это будет совсем легко. Самый простой способ добавить наш звук – добавить компонент Audio Source непосредственно в триггерную область игрового объекта. Давайте найдем LeftStairsTrigger и прокрутим вниз в инспекторе до скрипта Interaction Trigger, как показано на рис. 10.20.
Рис. 10.20. Скрипт Interaction Trigger для игрового объекта LeftStairsTrigger
Если вы помните, мы создали UnityEvent под названием OnInteract, который можно использовать с нашим компонентом Audio Source! Нажмите Add Component в нижней части инспектора и выберите Audio Source. Затем перетащите файл StairsPuzzleSuccess.wav в компонент Audio Source. Оставим Audio Source как 2D, так как звук, который мы воспроизводим, является обозначением прохождения. Теперь щелкните + в UnityEvent OnInteract и в поле с надписью None (Object) перетащите компонент Audio Source, как показано на рис. 10.21.
Рис. 10.21. Звук, добавленный к триггеру взаимодействия
Далее вы увидите раскрывающийся список, который в настоящее время помечен как No Function. Щелкнем здесь и перейдем к AudioSource, а затем к Play(), как на рис. 10.22.
Запуск звука через взаимодействие с персонажем 231
Рис. 10.22. Добавление метода Play к звуковому объекту при взаимодействии
Это обеспечит воспроизведение нашего аудиофайла при активации LeftStairsTrigger. Нажмите кнопку воспроизведения и перейдите к LeftStairsTrigger. Как только вы это сделаете, вы услышите звук! Давайте продолжим и повторим тот же самый процесс для RightStairsTrigger.
Звуки вращения деталей головоломки Сейчас мы будем запускать звук непосредственно из кода. Это будет довольно простой процесс сделать нашу переменную Audio Source общедоступной через код. Затем мы просто запускаем его. Добавим следующие звуки: звук, который воспроизводится, когда головоломка завершена, звук, когда шпиль начинает двигаться.
232 Звуковые эффекты Начнем с самого простого, с нашего звука «завершение головоломки». Он запустится, когда все шпили будут выровнены, и дверь откроется. Перейдите в префаб First Puzzle в нашей сцене и откройте скрипт FirstPuzzle.cs. В этот сценарий мы добавим код, как показано на рис. 10.23. В строке 173 введите: public AudioSource puzzleCompleteSFX;
Рис. 10.23. Public Audio Sourcе добавлен к первому скрипту головоломки
Теперь вернитесь к префабу First Puzzle в сцене, откройте инспектор и добавьте компонент Audio Source. На этом источнике звука мы снимем флажок Play on Awake и перетащим в него файл FirstPuzzleJingle.wav. Затем таким же образом, как мы перетаскивали наш аудиокомпонент в UnityEvent, мы перетащим Audio Source в новое сериализованное поле с меткой Puzzle CompleteSFX в нашем скриптовом компоненте FirstPuzzle, как на рис. 10.24.
Рис. 10.24. Перетаскивание аудиофайла в компонент Audio Source
И последний шаг – перейти к функции CheckForVictory() внутри скрипта FirstPuzzle.cs и к оператору if в строке 241. Прямо перед return true в строке 245 на рис. 10.25 мы добавим следующее:
Рис. 10.25. Добавление Play к источнику звука
Заключение 233 Теперь давайте зайдем в игру и посмотрим, работает ли она. Когда мы вой дем в игру, то сможем активировать нашу головоломку и услышать звук, когда успешно вращаем шпили!
Головоломка с деревом Используя тот же метод, что и раньше, давайте добавим звук, который воспроизводится, когда мы кладем мяч на мост, решаем часть головоломки и когда завершаем последнюю головоломку. Откроем FinalPuzzle.cs и добавим: IntroPuzzleSolved.wav в строке31, FinalPuzzlePartial.wav в строке 38, FinalPuzzleSolved.wav в строке 41.
Заключение Поздравляю! Мы только что сделали первые шаги к осознанию звука в видео играх. Мы рассмотрели, что составляет звуковой эффект, разобрали пять частей звукового дизайна, узнали об аудиослушателях и различиях между музыкой и звуком. Узнали, как использовать 3D-звук и как запускать компоненты Audio Source с помощью кода! Это отличное начало для того, чтобы вдохнуть жизнь в нашу игру через звук. В главе 12 «Последние штрихи» во время полировки звука мы дадим несколько дополнительных советов, которые помогут вашему звуку продвинуться еще дальше. В следующей главе перейдем к сборке нашего проекта, чтобы вы могли поделиться им с другими.
11 Сборка и тестирование На пути к данному этапу разработки мы вместе проделали большой объем работы. Теперь у нас должен быть вертикальный срез игры, в которую до сих пор можно было играть лишь в редакторе Unity, и она работает. Это замечательно, но вы же не собираетесь ожидать, что ваши игроки загрузят Unity и откроют пакет, а затем воспроизведут его в редакторе? Думаю, нет! Здесь мы хотим преобразовать игровой проект в исполняемый файл. В этой главе мы рассмотрим, как создать вашу игру, чтобы ее можно было опубликовать, протестировать и в конечном счете она попала в руки игроков. Вы узнаете о: сборке из Unity; тестировании – функциональном, производительности, игровом тестировании, погружении и тестировании локализации; пользовательском опыте (UX).
Сборка из Unity Мы очень усердно работали, чтобы собрать имеющийся опыт. Теперь нам нужно донести это до людей. Для этого необходимо сообщить Unity пару вещей. Unity должна знать, для чего вы строите; например, какие сцены должны быть созданы в приложении, на какой платформе и какие дополнительные параметры влияют на исполняемый файл вывода сборки. То, где мы сейчас находимся в вертикальном срезе, является хорошим местом для сборки. В каждом проекте этот момент может быть другим. В большинстве случаев лучший способ работы со сборкой таков: собирай раньше, собирай часто. В нашем случае нужно было подождать, пока у нас не будет какой-то механики и стандартного прохождения двух основных головоломок, прежде чем мы решили приступить к сборке. На рис. 11.1 вы можете увидеть меню Build Settings, которое находится в File > Build Settings. Под изображением мы разберем каждую из этих настроек.
Сборка из Unity 235
Рис. 11.1. Build settings
Первый блок, который мы видим, – это Scenes In Build. Поскольку он находится наверху, у нас должно быть ощущение, что это важно. Здесь автоматически помещается сцена по умолчанию в поле, но могут быть и другие сцены, которые вы хотите. У вас может быть другая сцена для систем меню или другая карта, которая может быть учебным уровнем. Ключевым фактором здесь является наличие в этом поле сцен, которые вы хотите видеть в игре; вы можете просто перетащить сцены из окна проекта в поле Scenes In Build.
Верхняя сцена в этом списке всегда будет первой загружаемой сценой.
Под блоком Scenes In Build графический интерфейс разделен на две части: Platform и настройки для этой платформы. Слева мы выбираем, для какой платформы все это делаем. Далее, настройки для выбранной платформы появятся справа. Мы рассмотрим только варианты для ПК, Mac и Linux. Если вы создаете для любой другой платформы, документация Unity поможет вам в процессе сборки. Ниже мы опишем большинство доступных параметров. Варианты для консолей и мобильных устройств будут иметь несколько различных доступных параметров, соответствующих потребностям их целевой платформы.
Target platform Этот вариант достаточно прост: на какую платформу вы хотите ориентироваться в этой сборке? Выбор здесь – Windows, macOS и Linux. Мы же собираем вертикальный срез для Windows.
236 Сборка и тестирование
Architecture Нам нужно знать, какую архитектуру процессора следует использовать. 32-разрядная ОС потребует от вашей игры менее 4 Гб ОЗУ. Вы можете выбрать это, но также можете использовать 64-битную версию для небольших игр, что не повредит вашей игре. В общем, 64-битная версия должна быть.
Сервер Unity может создать сервер для вашей игры, если вы работаете над многопользовательской игрой, создаст настройки плеера без визуальных элементов. Unity также будет создавать управляемые скрипты, определенные для многопользовательской игры. Мы не будем работать с этим, но знайте, что такой вариант есть. Также не будем рассматривать многопользовательскую разработку с помощью Unity, поскольку с самого начала это будет совсем другой проект.
Copy PDB files Параметр только для платформы Windows. Он позволит вам создать базу данных программы Microsoft для отладки. Мы не будем использовать это в нашей сборке.
Create Visual Studio Solution Данный параметр также только для платформы Windows. Включение этого параметра позволит вам выполнять сборку из Visual Studio, а не только из меню Build Settings. Если вы ориентируетесь на macOS, вместо этого будет установлен флажок Create Xcode Project.
Development Build Включение этого параметра позволит выполнять отладку, включая Profiler. Profiler – это анализатор, позволяющий узнать, что выполняется во время выполнения. Мы подробно рассмотрим это в разделе «Тестирование» этой главы. Есть также определенные настройки, которые будут включены. Очень удобно, когда вам нужно протестировать приложение и вы не беспокоитесь о производительности. Особенно важно помнить об этом, если у вас ограниченный визуальный бюджет. Существует термин «бенчмаркинг» (benchmarking). Он относится к тестированию вашей сборки на целевой машине. Если вы выбрали для тестирования более дешевый компьютер, обратите внимание на его характеристики и создайте возможность игры в режиме разработки, чтобы вы могли запускать Profiler во время работы. После того как у вас есть эталонный тест, можете сделать некоторые обоснованные предположения о том, как он будет работать на машинах более высокого и более низкого уровня.
Autoconnect Profiler Вы можете включить этот параметр, если у вас включен Development Build. Он автоматически подключит Profiler, о котором мы говорили выше в разделе Development Build.
Сборка из Unity 237
Deep Profiling Support Это также требует, чтобы сборка разработки была включена. Так Unity Profiler сможет записывать более подробные данные. Не лучший вариант для проверки производительности, так как может быть некоторое замедление выполнения скриптов. Основная цель сборки с глубоким профилированием – выяснить конкретную причину управляемых приложений путем записи всех вызовов функций. Поскольку каждый метод будет записываться отдельно, глубокое профилирование дает очень четкое представление о том, что и когда вызывается. Поэтому некоторые ошибки во время игрового процесса легче обнаружить и выявить.
Script Debugging Включение этого также требует включения Development Build и добавления символов отладки в код скрипта. Так вы сможете подключать IDE (интегрированный редактор разработки, такой как Visual Studio) к игре, когда она запускается, для ее отладки с помощью точек прерывания и систем отладки. Когда вы выберете это, появится другая опция, как показано ниже на рис. 11.2:
Рис. 11.2. Параметр Wait For Managed Debugger
Параметр Wait For Managed Debugger, если включен, будет ждать, пока IDE не найдет сборку, чтобы запросить подключение. Никакие скрипты не будут выполняться, пока не установится соединение с отладчиком.
Scripts Only Build Может наступить момент, когда вы обнаружите некоторые ошибки, соответственно, нужно будет внести исправления, но вряд ли захочется создавать все подряд. Файлы данных могут быть очень большими. Ранее в книге мы говорили о том, что итерация важнее всего. Это может значительно сократить время между итерациями отладки. Эта опция будет создавать только скрипты и сохранять все файлы данных нетронутыми.
Метод сжатия Здесь есть три варианта: Default, LZ4 и LZ4HC. Default означает отсутствие сжатия. Никакое сжатие не позволит сразу запускать файл в Windows, Mac и Linux. В сборках Android он будет создан в виде ZIP-файла. LZ4 полезен для разработки сборок, поскольку сохраненные данные будут сжаты и распакованы или распакованы во время выполнения. Загрузка сцен и ассетов зависит от скорости чтения с диска. Это еще один параметр, который можно использовать для увеличения скорости итерации, поскольку время сборки меньше, чем по умолчанию. Интересно отметить, что распаковка LZ4 на Android быстрее, чем ZIP по умолчанию.
238 Сборка и тестирование LZ4HC – это версия LZ4 с высокой степенью сжатия, сборка которой займет больше времени, поскольку она еще больше сжимает сборку. Это отличный вариант для релизных сборок после пройденного времени на отладку. Начать с Default для быстрых тестов при тестировании гейм-плея – хорошая идея. Дойдя до момента, когда вам нужно разработать сборку и отладку, используйте LZ4. Затем, когда вы будете готовы к релизу, используйте LZ4HC.
Тестирование Тестирование игры – широкое понятие. Есть большие методы тестирования, которые являются более распространенными, и некоторые более мелкие, более конкретные. Наиболее распространенные шаблоны тестирования, которые мы имеем: функциональность, производительность, плейтестинг, погружение, локализация. Если вы исследуете QA игр или тестирование игр, вы найдете несколько других названий для тестирования, и студия может иметь свое собственное конкретное тестирование, которое является их передовой практикой. Все имеет место быть. Тестирование, которое мы объясним из списка выше, можно увидеть почти в каждой студии. Давайте разберем список.
Тестирование функциональности По правде говоря, ваше тестирование началось задолго до того, как вы добрались до этой главы. Каждый раз, когда вы нажимали кнопку воспроизведения, чтобы проверить, делает ли скрипт то, что должен делать, вы тестировали этот скрипт вместе с остальной игрой. Это часть итеративного характера разработки игр, также называемая тестированием функциональности. Тестирование функциональности имеет очень характерное название! Очевидно, что это тестирование функций игры. Вот несколько примеров объема тестируемых функций. Анимация – ищем анимации, которые не работают вместе, или сломанные риги персонажей. Осуществляется путем тестирования механики и движений, когда анимация персонажа переходит к другим анимациям. Звуковые элементы и качество – внимательное прослушивание с целью выявления дефектов звучания в определенное время. Примерами этого может быть прослушивание звуков шагов, которые, возможно, звучат неправильно, лишние окружающие шумы для объектов, которых нет в среде, и любое место, где звук неправильно расположен. Синематики – просматривайте ролики, чтобы найти любые неуместные звуки, визуальные эффекты, анимацию или синхронизацию всего ролика. Инструкции или туториалы – могут быть инструкции о том, как играть в игру. Эти инструкции должны быть написаны правильно и соответствовать схеме контроллера, которую использует игрок.
Тестирование 239 Взаимодействие с механиками – играйте со всеми механиками, чтобы почувствовать их, проверить, работают ли они так, как задумано, и можно ли их завершить, если есть завершение. Сортировка – это визуальная проверка, касающаяся вопросов прозрачности. Слои на экране должны знать свое место на экране. Некоторым эффектам и пользовательскому интерфейсу будет трудно понять, что находится сверху, чтобы правильно отсортировать их. Это требует тестирования с несколькими скриптами, чтобы убедиться, что игровые объекты с прозрачностью сортируются правильно. Удобство использования – тут, в свою очередь, есть свой собственный поток работы, который можно протестировать, но в данном случае мы ищем схемы контроллеров, имеющие смысл и работающие. Примером может служить кнопка A, используемая для прыжка; это взаимодействие настолько распространено и часто применяется на практике, что, если используется что-то другое, необходимо будет подробно объяснить, почему. Пользовательский интерфейс (структура меню, разрешение, соотношение сторон, размер шрифта) – пользовательский интерфейс необходимо тщательно проверить по многим частям. Как он выглядит в масштабе? Цвета выглядят правильно? Вы понимаете, как выглядит меню? Каждая небольшая проблема, которая появляется, будет замечена большинством пользователей. Они должны быть задокументированы для исправления. Как видите, тестирование функциональности является исчерпывающим, и его необходимо повторять с целью быть увереным, что все игровые функции понятны игроку и работают правильно. Когда вы играете в игру, чтобы протестировать одну механику, это отличная практика, но всего лишь проверка одной звезды в космосе. Вносимые вами изменения потенциально могут повлиять на остальную часть игры. Тщательное тестирование функциональности, проведенное заранее и множество раз, сделает ваш проект намного чище и позитивнее в конце. Во время тестирования функциональности вы можете столкнуться с задержками при рендеринге, из-за чего частота кадров будет низкой. Если это произойдет, примите к сведению и добавьте это в список тестов производительности, которые также будут выполнены. Раз уж мы затронули эту тему, давайте рассмотрим, как тестировать производительность в Unity.
Тестирование производительности Внутри Unity есть четыре источника анализа: Unity Profiler, Memory Profiler, Frame debugger, Physics debugger и модуль Profiler.
Unity Profiler Чтобы выполнить профилирование, вам понадобится инструмент анализа Unity Profiler, который выглядит так, как показано на рис. 11.3 ниже:
240 Сборка и тестирование
Рис. 11.3. Пример окна Unity Profiler
Инструмент профилирования помогает определить использование ЦП, использование памяти и время рендеринга. Когда вы откроете Profiler, выбрав Window > Analysis > Profiler, вы увидите четыре раздела, как показано на рис. 11.3 выше. (Красный) Profiler modules – в этом разделе указаны используемые модули профиля, а также цвета, показывающие, что происходит, когда начинается запись профилировщика. (Оранжевые) Profiler controls – эти элементы управления предназначены для настройки того, что делает Profiler. Здесь вы можете начать запись и изменить режимы или инструмент Profiler. (Желтый) Диаграммы кадров – показывает отдельные кадры в виде диаграммы во времени и кривых для проходов рендеринга. (Зеленый) Панели сведений о модуле – на панелях сведений о модуле поясняется каждый раздел выбранного кадра с разбивкой по процентам использования в запрошенном потоке. По умолчанию это основной поток. Например, в профиле ниже на рис. 11.4 у нас есть кадр, выбранный во время игры; я записал 7800 кадров, перемещая Мивари. Вы можете видеть, что в настоящее время мы работаем со скоростью около 60 кадров в секунду. Я хотел узнать, что отнимало у PlayerLoop больше всего процессорного времени – в данном случае это была игра, запускаемая в редакторе на ~80 %. Прокручивая PlayerLoop вниз, мы видим, что прямой рендеринг – самая тяжелая задача в основном потоке. В настоящее время на сцене мало что происходит, поэтому мы используем в среднем 60 кадров в секунду.
Рис. 11.4. Профилировщик с выбранным кадром
Тестирование 241 Мы видим, сколько информации поступает вместе с Profiler. Это фантастическая информация, когда вы хотите узнать, что может быть причиной более низкой частоты кадров в вашей игре.
Memory Profiler Этот инструмент, как и ожидалось, профилирует память редактора. Вы также можете запустить профилировщик памяти в автономной сборке, если в меню Build Settings выбран пункт Development Build. Ограничение здесь заключается в том, что вы не можете запустить профилировщик памяти в релизной сборке, как и в случае с Unity Profiler, который мы видели ранее. Профилировщик памяти также не добавляется в проект Unity автоматически. Его можно добавить. Для этого следуйте инструкциям ниже. Чтобы добавить профилировщик памяти, перейдите в диспетчер пакетов и добавьте пакет по имени (add package by name). Это можно сделать, выбрав параметры, как показано на рис. 11.5, а затем добавив com.unity.memoryProfiler в поле Add package by name:
Рис. 11.5. (Слева) Add package by name, (справа) Adding the Memory Profiler
Когда он установлен, вы можете получить доступ к профилировщику памяти, выбрав Windows > Analysis > Memory Profiler. При первой загрузке вы увидите пустой средний раздел с возможностью создания моментального снимка. Если вы нажмете кнопку воспроизведения, а затем сделаете снимок, вы получите экран, аналогичный тому, что мы имеем на рисунке ниже. В этом Profiler содержится большое количество информации. В основном это используется, если у вас низкая частота кадров и вы хотите проверить использование памяти или если во время теста на погружение вы видите сбои. Вы можете видеть через несколько моментальных снимков во времени, какая память используется. При просмотре снимка центральная область разбивает на части всю память. В нижней части есть блоки всего, что используется в группах. Вы можете выбрать группы, чтобы разбить их на еще больше частей. Мы проделали это с нижним блоком розовато-лилового цвета, который используется для отображения Texture2D. Мы уже знали, что это будет иметь большой объем памяти, поскольку это текстурирование для всей архитектуры. Объем памяти должен быть достаточно большим. Если память выглядит так, как вы ожидали, загляните в отладчик кадров и проверьте, что может загружаться в определенных кадрах и вызвать проблемы. Давайте рассмотрим этот инструмент дальше.
242 Сборка и тестирование
Рис. 11.6. Memory Profiler
Frame debugger Возможность видеть отдельный кадр и то, как построены вызовы отрисовки, чтобы дать вам визуализацию этого кадра, может быть очень полезна для отладки визуальных артефактов. Возможно, вы использовали это, когда проводили тестирование функциональности и обнаружили проблемы с сортировкой. Теперь можете загрузить отладчик кадров и посмотреть, когда был отрендерен каждый элемент. С этого момента у вас будут знания и понимание, почему он был отрендерен в неправильном порядке.
Рис. 11.7. Frame debugger
Еще одна сильная особенность отладчика кадров заключается в том, что вы можете видеть, какие свойства шейдера были установлены для визуализируемого элемента, как показано на рис. 11.7 выше. Это полезно, поскольку, когда вы устанавливаете свойства шейдера процессуально, может быть ожидание свойств шейдера с использованием определенных текстур переменных. Если они не такие, как ожидалось, было бы неплохо проверить уста-
Тестирование 243 новленные значения кадра. Это может привести к обнаружению того, что для одного кадра было установлено то, что вы ожидали, но затем оно было переопределено. Это может помочь вам найти правильный скрипт, который изменяет свойства шейдера через один или два кадра после установки ожидаемых значений.
Physics debugger и модуль Profiler Для физики у нас есть и отладчик, и модуль Profiler. Отладчик физики – это инструмент визуализации, позволяющий узнать, какие физические коллизии есть в сцене и с чем они должны или не должны сталкиваться. Как вы видели в главе 7, Rigidbody сложно настроить для оптимизации физических свойств. Возможность визуализировать, какой тип коллайдера находится на чем и где, очень помогает узнать, что делают объекты, когда и почему. На рис. 11.8 ниже вы можете видеть в сцене, какие объекты являются физическими. Открытие раздела Colors отладчика физики позволит вам раскрасить их в соответствии с тем, что вам может понадобиться для отладки:
Рис. 11.8. Physics Debug
После того как вы определили любые физические проблемы с GameObjects, визуализированные отладчиком физики, есть еще один инструмент для сбора дополнительной информации. К счастью для нас, также есть модуль Physics профилировщика, способный помочь нам с любыми проблемами c физикой, которые визуализируются, чтобы мы могли идентифицировать проблему. Модуль находится в Unity Profiler и поможет найти ответы на расхождения в физике, которые вы можете увидеть при включенном отладчике. Чтобы увидеть, как выглядит модуль физики профилировщика, см. рис. 11.9 ниже:
244 Сборка и тестирование
Рис. 11.9. Модуль Physics
Использование данного модуля может быть непонятным на данный момент, поскольку у нас нет нужного случая с нашей физической механикой, чтобы показать прямую проблему. У нас нет быстро движущихся предметов, которые могут вызвать много проблем с физикой. Если в вашей игре есть быстро движущиеся объекты, когда вы записываете свой Profiler и замечаете, что игровые объекты пересекают другие игровые объекты, когда они не должны, этот модуль будет полезен для просмотра общего объема используемой памяти. Возможно, используемая память не позволяет физике обновляться достаточно быстро, чтобы получить информацию об активном объекте. Отладка физики может занять некоторое время, но зато вы получите нужный результат. Будьте терпеливы с этой отладкой и используйте как можно больше инструментов. Теперь, когда мы рассмотрели все инструменты отладки, нужно протестировать игру с другими людьми, не входящими в нашу команду. Это называется плейтестинг. Давайте отдадим игру в чужие руки.
Плейтестинг Это трудная для оценки проблема. Сначала может быть хорошей идеей провести внутреннее тестирование вместе с вами и вашей командой, друзьями и союзниками. Вы просите их сыграть в игру и посмотреть, как они к ней относятся. Обратите внимание, это не предназначено для создания вашего эго. Вы должны быть там, когда они играют, и попросить их вслух рассказать о том, что они думают о получаемом опыте. Не подсказывать им ни в чем. Необходимо, чтобы вызывались искренние чувства, и, если они говорят о предполагаемом опыте, вы на правильном пути. Даже если игровое арты не на месте, а системы меню представляют собой блочные объекты с замещающим шрифтом Arial, ничто из этого не умаляет основного игрового опыта. Одним из примеров является висцеральная реакция, когда кто-то входит в первую эмоциональную точку соприкосновения, которую вы хотите вызвать. В нашем проекте мы хотим создать ощущение чуда. Это близко к путанице, поэтому нам нужно быть обдуманными в размещении и легком использовании, чтобы поощрять приключения на условиях игрока.
Тестирование 245 Если они говорят такие вещи, как «интересно, что там», это вызывает чувство заинтересованности. При этом они могут сказать ту же фразу, но ничего подобного в этом месте не было запланировано. Пусть исследуют все, что, по их мнению, можно исследовать, и примут к сведению. Позвольте этому неотъемлемому удивлению продвинуть ваш дизайн дальше, чем вы уже зашли. Не особо важно, что там будет, если это какое-то взаимодействие. Примером является область, не имеющая ничего общего с повествованием, которая лишь немного отодвинута в сторону, и в нее можно двигаться. Когда тестировщики перемещаются в места на карте, которых вы не ожидаете, используйте это как возможность для взаимодействия. Вы можете построить, например, живописный вид. Когда вы идете туда, камера немного выдвигается, чтобы заснять красивый вид. Это не дополнение к истории, но чувство чуда было исполнено. Исследование может не занимать большую часть вашей игры; может быть, ваша должность связана с социальным опытом. Потребность во взаимодействии высока для любого игрока, находящегося внутри многопользовательской игры. Каким образом вы можете взаимодействовать с друзьями, играя вместе? Чрезвычайно хорошим примером этого является многопользовательская система взаимодействия FromSoftware. Социальное взаимодействие в игре FromSoftware – например, Elden Ring – позволяет вам оставлять короткие сообщения с определенными словами, но вы также можете использовать эмоции, и сообщение будет изображать вашего персонажа как призрака с небольшим сообщением и эмоцией. Это позволяет взаимодействовать с эмоциями, которые вы, возможно, испытали. Это очень интересный способ взаимодействия в игре, который определяется ее способностью заставлять вас чувствовать себя одиноким и слабым. После того как они закончат играть, сделайте все свои заметки и поблагодарите их за уделенное время. Вам не нужно реализовывать абсолютно все, что вы записали из их теста, но после того, как человек пять протестируют, появятся новые тенденции. В первую очередь позаботьтесь об этих тенденциях.
Продолжительное тестирование Продолжительное тестирование – мы просто позволим игре работать 24 часа без дела. Ваш персонаж в игре, живой, просто сидит сам по себе, пока игра разворачивается. Таким образом выявляются любые возможные утечки памяти в игре. Утечка памяти – это когда есть какая-то память, о которой где-то не позаботились. Допустим, у нас есть частица, падающая с дерева для создания приятной атмосферы. Частица настроена на смерть, но случайно была добавлена пара дополнительных нулей, так что теперь вместо 10 секунд она длится 1000 секунд. Это может не быть проблемой, когда вы бегаете в игре, ведь когда вы выходите, частицы будут отбрасываться. Но если вы позволите игре бездействовать, все системы частиц, создающие листья, будут накапливаться, и несколько тысяч листьев на земле могут сильно снизить производительность. Потребовались бы исправления, и это было бы невозможно без продолжительного тестирования.
246 Сборка и тестирование
Тестирование локализации Локализация – это процесс перевода игры на другой язык. Данный тест может занять больше времени, чем ожидалось, и вам нужно набраться терпения. Для этой части тестирования необходимо рассмотреть каждый пункт меню, строку диалога и описание. Перевод – это не просто дословное объяснение. Некоторые языки требуют большего контекста для своих описаний, что может привести к очень запутанным переводам, если не обратить на них пристального внимания. При локализации игры постарайтесь не торопить этот процесс. Это может полностью отбросить опыт другой культуры, что было бы ужасно!
Пользовательский опыт, или UX UX можно определить как сумму частей брендинга, дизайна и удобства использования. Со своей стороны мы кратко расскажем, как брендинг играет свою роль в UX. Затем совсем немного поговорим о дизайне, так как мы уже рассмотрели дизайн основных частей этого проекта. А затем сможем перейти к теме удобства использования. Давай сделаем это!
Брендинг Чтобы охватить это в широком смысле, брендинг через призму UX – это то, как общий опыт, который пользователь получит в процессе игры, также должен быть отражен в брендинге. В качестве чрезмерно контрастного примера подумайте о том, использовались ли в брендинге игры ужасов мягкие тона и пастельные тона с цветами и веселой музыкой в качестве маркетингового материала. Это явно не соответствует брендингу игры и вызовет диссонанс в пользовательском опыте. Цель и смысл UX как определенной части разработки заключается в том, что внимание уделяется преднамеренным сплоченным действиям. Время, потраченное на UX, должно гарантировать, что все разработанные части, включая логотип, маркетинговые материалы и игровые части, являются частью единого, цельного опыта.
Дизайн До сих пор в этой книге мы разбирали множество аспектов дизайна. Интересно, что мы рассмотрели весь наш дизайн немного разрозненно. Иногда это может вызывать проблемы, но, к счастью для нашего проекта, мы тщательно сосредоточились в дизайне персонажей. Остальная часть дизайна была построена вокруг прошлого расы Мивари, что давало ответы на вопросы по визуальным подсказкам. Темп игры определяется стилем игрового процесса и механикой, ориентированной на повествование об окружающей среде. Объединение этих трех частей уже превратилось в цельный проект. Это хорошо подтверждает время, которое мы потратили на дизайн каждой части. Хорошая работа, придерживайтесь такого принципа!
Пользовательский опыт, или UX 247
Удобство использования После того как вы зацепите пользователя своим изысканным брендингом, а также интеллектуальным и связным дизайном, он сможет использовать продукт. В играх удобство использования сосредоточено на взаимодействии. Это не должно вызывать большого удивления, поскольку мы определили это как столп опыта в главе 6. Мы проработали общее взаимодействие с игроком; однако определенная доступность отсутствует. Нам нужно проработать, как игрок узнает, что он может выполнять действия. Здесь мы рассмотрим основные части вертикального среза, начиная с начальной проблемы открытия первой головоломки, а затем перейдем непосредственно к самой первой головоломке. После этого нам нужно рассказать о том, как будет представлена следующая механика, телекинез и, наконец, финальная головоломка.
Исходная проблема В начальной части пещеры у нас есть заблокированная лестница, где игроку нужно взаимодействовать с двумя объектами, чтобы разблокировать ее. Есть несколько вещей, которые мы будем использовать, чтобы предоставить игроку доступность и понимание, как выполнить необходимую задачу: нацеленное освещение, мировой UI, удовлетворение действия. Нацеленное освещение – это небольшая часть дизайна окружения или уровня, которая позволяет игроку почувствовать, что он должен двигаться в этом направлении. Если туннель темный, а в конце есть свет, игрок будет стремиться направиться к этому свету. Мы можем использовать этот метод, чтобы иметь светящиеся объекты или источники света рядом с кнопкой, которую нужно нажать. В таком случае, когда они наведут курсор достаточно близко, мы отобразим пользовательский интерфейс мирового пространства для взаимодействия. Эта кнопка должна быть такой же, как и любое другое взаимодействие подобного типа. Конкретно в нашей игре взаимодействие – это клавиша E на клавиатуре. После того как вы используете интерактивную кнопку, должно быть что-то, что удовлетворяет использованию этого действия. В данном случае это кнопка в камне. Он будет анимироваться, чтобы установить себя на нужное место, светиться от использования и вызывать звук, чтобы полностью указать, что пользователь использовал свои возможности.
Первая головоломка Когда вы впервые подходите к первой головоломке, может быть не сразу понятно, что цель игрока состоит в том, чтобы переместить камни в правильную позицию. Мы использовали одну первичную ключевую функцию удобства использования среди других, которые могут показаться похожими. Перечислим их еще раз: нацеленное освещение, чувство героизма, мировой UI, позиционирование камеры (ключевой фактор, упомянутый ранее), удовлетворение действия.
248 Сборка и тестирование Ранее мы рассматривали нацеливание источников света как концепцию позиционирования, целью которого является привлечение визуального внимания к месту, куда нужно двигаться. На что нужно обратить внимание, так это на дверь, так как в ней есть ответ на загадку перед вами. Чувство героизма, к которому мы стремимся, является ответом на загадку. Оно находится непосредственно в следующей позиции, куда вы направляетесь, и прямо перед вами, когда вы выходите из лестницы. Нет никаких препятствий для входа в то, на что вы должны смотреть, когда доберетесь до вершины лестницы. Это то, что мы любим делать для наших игроков. Пусть они почувствуют себя исследователями, но знайте, что при этом они смотрят в нужное место. Наш пользовательский интерфейс в мировом пространстве такой же, как и раньше, но мы используем его, чтобы игрок знал, что он может взаимодействовать с частью головоломки, когда он приближается к подвижным частям. Ключевым фактором для перемещения в следующей части является движение камеры. Когда вы войдете в пространство головоломки, камера плавно переместится в положение, которое будет представлять успешное движение столбов в соответствии с произведением искусства на двери, когда вы впервые вошли. После того как игрок перемещает столбы на место, раздаются громкие звуки складывания камней и соединения того, что может звучать так, как будто большой тумблер замка встает на место. Когда последний столб находится на месте, вся деталь перемещается в окончательное завершенное положение, и камера возвращается за плечо, в то время как столб в середине поднимается, позволяя игроку нажать кнопку «открыть дверь».
Введение во вторичную механику Мы объясняли возможности игроков и то, как заставить их выполнять действия. Вы можете добавлять новые механики, не нарушая игровой процесс, если позаботитесь о том, чтобы представить их преднамеренно. До сих пор Мивари была только исследователем. Мы хотим, чтобы она обладала небольшими способностями к телекинезу благодаря своей древней крови. Мы могли бы просто активировать их и поставить что-то на ее пути, но это не очень весело, и опыт не такой мощный. Чтобы получить новую механику и, надеюсь, привлечь игрока к заботе о нашем главном персонаже, мы дадим два действия, которые сначала не связаны с персонажем напрямую. Первое, что мы сделаем, – это, прогуливаясь по тонкому участку на склоне горы, увидим большой камень, падающий с горы. Произойдет кинематографический ролик, в котором Мивари будет встревожена, и она будет защищаться, подняв руку, что лишь слегка активирует ее телекинез, и она немного сдвинет камень в сторону, чтобы он рухнул с горы. Затем после небольшого исследования игрок натыкается на область, куда не может войти, но может ее видеть. Есть столб, похожий на тот, что открыл первую дверь из первой головоломки, но он отделен. Мы будем использовать новую форму пользовательского интерфейса мирового пространства, и, когда вы наведете курсор на сломанный камень, он будет выделяться, и появится кнопка взаимодействия. Этот контур будет иметь цвет, аналогичный тому, который использовался для телекинеза с камнем, и когда вы взаимодействуете с ним,
Пользовательский опыт, или UX 249 вы протягиваете руку и поднимаете его. Когда он окажется достаточно близко к столбу, он автоматически починится, что вызовет множество визуальных изменений в области, в которой вы находитесь. В заключении мы используем небольшие примеры телекинеза, чтобы представить это так, как подобает персонажу. Вот это хорошее использование удобства использования, поскольку игрок познает вместе с персонажем. Теперь можно взять то, что узнали здесь, и применить это к финальной головоломке, в которой телекинез используется в качестве основной механики.
Финальная головоломка Интересно, сколько усилий требуется, чтобы сделать опыт мощным! Все эти предварительные усилия по описанию механики являются связующим звеном, которое превращает опыт в предмет погружения, а не просто еще в одну кнопку, которую нужно нажать, чтобы заставить персонажа что-то сделать. Теперь мы приступаем к последней головоломке, и есть аналогичные средства для возможностей игроков, которые вы видели ранее. Мы будем использовать немного другие средства, поскольку элементы, используемые в механике, отличаются, но общая концепция определяется окружающей средой. Вы увидите эти функции удобства использования UX в последней области головоломки: нацеленное освещение, связь с произведением искусства, мировой UI, удовлетворение действия. Как всегда, мы используем освещение, чтобы помочь нашим игрокам понять следующий ход. Из-за нашего главного элемента (основной фокус) дерева исходит большой свет, а это означает, что он имеет повествовательное значение и нуждается в более авторской обработке, которая показывает свечение, исходящее от каждой ветви, прикрепленной к архитектуре. Если следовать за ветвями, они проходят через большие столбы. Эти столбы связаны произведением искусства – центральным деревом. Дело в том, что в самих столбах чего-то не хватает. Это что-то находится на земле в нескольких местах вокруг дерева. Перемещение мыши, чтобы посмотреть на данные более крупные сферические объекты, показывает, что вы можете взаимодействовать с ними. В данном случае взаимодействие – пользовательский интерфейс мирового пространства. Это контур, который мы уже видели ранее, пересекая водный мост, когда мы взяли часть столба, чтобы завершить его. Если взять объект и переместить его рядом с пустым местом той же формы, ветви засветятся, что необходимо сделать в определенном порядке, чтобы подать питание к дереву. Помешать что-то здесь может, только если игрок не заметит, что некоторые ветви сломаны или подключены неправильно. Удовлетворение действия исходит от приятной энергии, текущей от ветвей к дереву при каждом размещении объекта. К концу дерево засветится в синематике и откроет область, в которой находится тиара. Мивари хватает тиару и надевает ее себе на голову, открывая ей место последней принцессы ее древней расы.
250 Сборка и тестирование На этом вертикальный срез заканчивается, когда после завершения головоломки портал включается, и она проходит через него, взволнованная следующим шагом чудесного приключения.
Заключение Вы можете подумать: «А что же дальше?» Отличный вопрос. У вас есть собранная игра, которая прошла тестирование с некоторыми исправлениями ошибок. Сейчас у вас играбельный элемент, способный предоставить инвесторам достаточно контекста, чтобы продолжить путь к финансированию или релизу игры. Далее мы рассмотрим методы полировки и поставим UX на первое место. Мы назвали следующую главу «Последние штрихи», поскольку знаем, что вертикальный срез находится в хорошем состоянии, осталось только добавить последние штрихи к нему. Уделите время следующей главе, чтобы увидеть все задачи, которые мы можем выполнить с целью продвинуть наш бренд и качество в игре.
12 Последние штрихи Добро пожаловать в главу «Последние штрихи»! Существует неправильное представление о том, сколько времени уходит на создание игр, и об общей сложности разработки игр. Эта глава послужит набором инструментов, которые помогут вам завершить ваши проекты. Это не просто следующий шаг, а скорее открытая коробка с инструментами, которые мы используем для полировки нашего вертикального среза. Интересной особенностью процесса полировки является то, что он охватывает добрых 80 % разработки игры, что может показаться необычным; однако, если вы обращали внимание на скриншоты во время разработки, то заметили, что на данный момент у нас нет полной игры с точки зрения потребителя. Механика работает, и игра на данный момент уже представляет собой опыт, но не полный. В этой главе: обзор, доработка ассетов, освещение, доработка звука.
Обзор Последние штрихи чрезвычайно важны для полного впечатления. Нам нужно взять то, что у нас есть, и затянуть все швы. Это делается несколькими способами. Свет и звук очень трудно доработать до этого момента. Могут быть исследования и разработки, но, если игра не сосредоточена на одной из этих двух тем, вы не получите окончательного освещения или звука, пока в игре не будут завершены ассеты, как показано в списке в следующем разделе. Вы правы, задаваясь вопросом, почему у нас была глава о звуке до этого. Мы хотели пройтись по основам звука в целом и познакомить вас с концепцией звукового дизайна и его реализацией в Unity. Над освещением можно было бы поработать заранее, чтобы создать настроение, но его необходимо будет завершить после того, как среда и направленное освещение будут четко определены и закреплены на месте. Опять же, если освещение и настроение будут присутствовать в опыте, то серьезные исследования и разработки освещения должны проводиться даже на начальных этапах разработки. Все разговоры об освещении помогут вам и на этом этапе, если это необходимо.
252 Последние штрихи Это будет работать следующим образом: будут определенные действия, которые мы рассмотрим во всех трех основных разделах этой главы. Эти действия специфичны для нашего проекта, но все равно могут помочь вам в ваших будущих проектах. Думайте о них как об инструментах, а не как об учебнике. Некоторые из них могут нуждаться в программировании, а некоторые нет. Поскольку большая часть механик в определенной степени запрограммирована, мы сначала сосредоточимся на доработке ассетов. Помните, как мы уже говорили, трудно получить свет и звук, если ассеты не готовы. Начнем с этого!
Доработка ассетов Раздел будет потрясающим. Есть так много замечательных художественных ассетов и последних штрихов, которые мы можем рассмотреть. Вот список инструментов, которые могут помочь вам в ваших проектах в будущем: стилизация ассетов, детализация нормалей, чистка архитектуры, блендинг текстур, беспорядок в окружающей среде, детализация меша, эффекты, синематики, вторичная анимация. Во всех разделась мы объясним, почему мы делаем что-либо для нашего проекта, что может помочь вам решить, нужно ли выполнять эти полирующие штрихи в ваших собственных проектах в будущем. После этого рассмотрим буквальные шаги, которые мы предприняли, чтобы вы могли увидеть, как они выполняются. Интересно, что фактические наши шаги могут быть не единственным способом. Лучше всего использовать эти действия в качестве концепции или отправной точки, поскольку потребности вашего проекта будут другими. Начнем последние штрихи со стилизации ассетов.
Стилизация ассетов Определяя художественный стиль, мы начинаем с широких мазков. Даже если вы потратите время на то, чтобы наметить художественное направление, как только вы доберетесь до этапа полировки, нужно будет пройти его заново, чтобы нанести последние штрихи. В нашем случае мы обнаружили, что ассеты недостаточно стилизованы, чтобы соответствовать нашему художественному направлению. Слово «стилизованный» используется очень часто и имеет право часто использоваться для игр, ведь оно означает «не выглядеть реалистично». В нашем случае мы хотим, чтобы стилизация делала все более иллюстративным по своей природе. Это означает, что нам нужно перенести все контрастные силуэты и цвета в текстуры. Нам также нужны более насыщенные линии в текстурах. Хороший пример в рамках нашего проекта – ожерелье Мивари. Это произведение искусства должно выделяться, поскольку оно является основным источни-
Доработка ассетов 253 ком телекинеза Мивари. Мы также знаем, что увидим его вблизи во время синематиков, поэтому необходимо выделить время на разработку такого фрагмента.
Рис. 12.1. Стилизация ожерелья Мивари
Чтобы иметь как можно большую согласованность внутри персонажа и мира, стилизация должна происходить во всех художественных произведениях. После того как стилизация будет завершена, некоторым моделям может потребоваться добавить мелкие детали. Мы называем их «детализация нормалей». Давайте пройдемся по ним сейчас!
Детализация нормалей Нормаль детализации иногда можно считать частью стилизации. В нашем случае мы хотели, чтобы это было выдающейся частью художественного направления в целом, поэтому рассмотрим это вне стилизации. Мы хотим подчеркнуть стилизованность силуэтов моделей, но также и придать самим материалам ощущение реализма. Кожа должна выглядеть как кожа, а кора дерева – как кора дерева. Ниже на рис. 12.2 у нас есть детализация нормали гриба, чтобы придать ему немного дополнительных нюансов. Левое изображение имеет базовые нормали и текстуру. На правом изображении детализация расположена поверх.
Рис. 12.2. Слева без детализации; справа с детализацией
254 Последние штрихи Детальные текстуры также интересны, поскольку они, как правило, являются более мелкими деталями мозаичной текстуры, которые не будут хорошо вписываться в саму текстуру из-за размера текстуры модели. Чтобы получить мелкие детали, мы наложили их в шейдере.
Рис. 12.3. Detail normal
Выше показан шейдер, который мы используем для нормали детализации на рис. 12.3. Разберем это, следуя точкам подключения данных и объясняя причины для каждой ноды. Для начала мы начнем с ноды UV. Нода UV устанавливает UV-пространство, которым вы будете манипулировать. Выпадающий список позволяет вам выбрать, какой UV-картой управлять. Поскольку мы используем основной канал UV, мы оставим его на уровне UV0. Возьмем выходные данные UV и введем их в ноду Swizzle. Нода Swizzle позволяет пользователям принимать входные данные и смешивать каналы для создания выходных данных с необходимым объемом данных. Вы заметите, что мы установили xy в качестве вывода. Наши входные данные – это линия соединения, которая относится к Vector4, также показанному на входе Swizzle. В этом случае нам нужны только красный и зеленый каналы, поэтому мы просто запрашиваем канал xy или rg и получаем выходную зеленую линию Vector2. Unity Shader Graph уже отбирает остальные каналы, поэтому нам это особо не нужно, но хорошая привычка – использовать только те каналы, с которыми вам нужно работать. Мы переносим этот вывод в ноду Multiply. Multiply – здесь мы используем параметр с плавающей точкой для настраиваемости UV в строке вместе с вводом Swizzle. Параметр Detail Normal Scale выставлен, поэтому мы можем позже внести изменения в инспекторе, настроив его под наши нужды. Вывод этого пойдет в UV-канал ноды Sample Texture 2D. Sample Texture 2D – другим входом в эту ноду является Detail Normal. Нам нужно убедиться, что для параметра Space установлено значение Tangent, поскольку позже мы будем воздействовать на касательные для восстановления нормали. Возьмем выходные данные и снова перейдем к Vector2, но другим методом, отличным от Swizzle. Мы будем использовать ноду Combine из отдельных каналов в Sample Texture 2D. Combine – взяв R и G из выходных данных Sample Texture 2D, мы объединяем их, чтобы создать Vector2, который семплирует желаемую текстуру и следует заданным нами UV-разверткам. Теперь нужно взять этот Vector2 в масштаб и сместить его в другой диапазон.
Доработка ассетов 255 Ноды Scale и Bias (с использованием умножения и вычитания) представляют собой базовую математическую функцию для преобразования диапазона (от 0 до 1) в диапазон (от –1 до 1). Мы делаем это, умножая на 2, а затем вычитая 1 из обоих векторов X и Y. Это важно для нас, так как мы можем захотеть, чтобы нормаль казалась вогнутой или входила в модель. Закончив эту функцию, мы перенесем вывод в Normal Reconstruct Z. Normal Reconstruct Z – цель этой ноды – получить правильные значения Z для ввода R и G из карты нормалей, которую мы выбрали в Sample Texture 2D. После этого есть еще три шага. Мы будем следить за отдельными цифрами для следующих шагов. Возьмем выходные данные этой ноды и переместим их в Normal Strength. Normal Strength – подключение к ноде Normal Strength – это нормали, которые мы получили в качестве выходных данных Normal Reconstruct Z. Существует также значение с плавающей точкой, для которого мы создали параметр Detail Normal Strength. Это можно увидеть ниже на рис. 12.4. Мы используем эту ноду, чтобы, если карта нормалей кажется слишком детализированной или визуально непривлекательной, можно было немного смягчить ее. Параметр, который мы установили во входе Strength, позволяет динамически устанавливать Detail Normal Strength для каждого материала.
Рис. 12.4. Normal Strength
Мы берем результат и помещаем его в ноду Normal Blend. Normal Blend – в конечном итоге мы хотим, чтобы эти нормали деталей были наслоены с нормалью самого меша, с чем поможет данная нода.
Рис. 12.5. Normal Blend
256 Последние штрихи Она выведет карту нормалей с обеими нормалями внутри данных. Затем мы поместим вывод в логический параметр ключевого слова, который мы назвали Detail Normal?. Логическое ключевое слово. Оно разработано таким образом, чтобы мы могли использовать Detail Normal, если необходимо. Поскольку этот шейдер используется во многих материалах, нам нужен способ исключить Detail Normal из необходимости, если в меше оно отсутствует. Мы сделали это, задав входные данные для параметра On смешанные нормали меша и нормали деталей. Если для него установлено значение Off, то будет принята только нормаль меша.
Рис. 12.6. Detail Normal, логическое ключевое слово
Выход затем будет поступать на вход Master Stack Normal. При создании материала, если вы хотите, чтобы была детализация нормали, все, что вам нужно сделать, – это выбрать флажок On в параметре Detail Normal?. Далее мы будем работать над чисткой архитектуры.
Чистка архитектуры Силуэты нынешних зданий могут выглядеть хорошо, но имеет ли смысл архитектура? Это интересная проблема дизайна со строительными формами. Нам нужно убедиться, что архитектура выглядит так, как будто ее построило живое существо. Это сложная задача, поскольку существ, которым мы хотим подражать, не существует! Существа вымышленные, а это значит, что нам нужно очень четко представлять себе путь, по которому мы идем, создавая для них архитектуру. Мы знаем, что они сосредоточены на неземных телах и концепциях времени. Формы пространства, планет и понятие времени должны принимать участие в силуэтах зданий и материалов. Скорее всего, это будет не полная переделка частей, а больше продвижение форм, чтобы язык архитектуры выделялся достаточно для соответствия культуре, для которой мы разрабатываем. Нам также нужно избавиться от некоторой геометрии, которая никогда не будет видна. Это для оптимизации игры, что очень важно. В играх если вы чего-то не видите, то и рендерить это не нужно. Поэтому мы делаем то, что называется backface culling. То есть, если бы вы посмотрели на заднюю половину сферы изнутри, она была бы невидимой. Внутренняя сторона объекта не визуализируется, поскольку ее не видно. Если бы вы этого не сделали, то сфере пришлось бы отображать все внутренние грани, что было бы пустой тратой драгоценного компьютерного времени; нам нужно визуализировать все остальное.
Доработка ассетов 257
Блендинг текстур При создании ландшафта или более крупных объектов, которые необходимо объединить, всегда есть шов, показывающий, что объекты представляют собой 3D-меши. Это распространенная проблема, и она может навредить погружению или испортить опыт, если не уделить этому должного внимания. Есть несколько способов проработать проблему. Вы можете добавить еще один меш поверх сплита. Вы также можете наслоить или перекрыть меши, сделав некий разрез в модели, чтобы игрок думал, что объект должен был быть сломан. Также можете выполнить блендинг текстур. Один из способов сделать это – использовать материалы Y-up. У них могут быть и другие названия, но я называю их так из-за использования оси Y-up для смешивания материалов. Мы просим шейдер смешивать положительные значения Y мировых нормалей. Затем используем это значение в качестве значения Lerp в нашем шейдере, где базовая текстура находится на канале A, а B – текстура мха или снега. Давайте посмотрим на рис. 12.7–12.9 ниже, где показаны скриншоты изображений Shadergraph. На рис. 12.7 мы показываем некоторые камни с одним набором UV-развертки и текстурой камня. Эти камни точно такие же, за исключением того, что мы продублировали их и повернули, чтобы показать созданный нами шейдер, который накладывает текстуру на мировые нормали.
Рис. 12.7. Камни с примененным шейдером Y-up
Примененные текстуры не являются окончательными текстурами мха, но они разработаны так, чтобы контрастировать с камнем и показывать текстуры отдельно. Так мы сможем легко работать с различиями в визуальных эффектах. Вы заметите, что камни такие же, но масштабированы и повернуты. Это надежный способ обеспечить повторное использование ваших мешей в сцене, поэтому вам не нужно моделировать так много камней! Далее давайте посмотрим на Shadergraph, как это работает.
258 Последние штрихи
Рис. 12.8. Мировая нормаль Y-up для значения T Lerp
Нам нужно спланировать, как мы будем разделять визуализацию текстуры на сетке. Интересно то, что нужно сделать так, чтобы текстура всегда появлялась в верхней части меша независимо от того, как он повернут. Мы решили взять вектор нормали мирового пространства, а затем умножить его на Vector3, который назвали Offset. Нам нужен положительный Y, поэтому значение по умолчанию параметра Offset будет (0, 1, 0). У нас есть еще два параметра смешивания. Это Blend и Level, и оба они плавающие. Параметр Blend является жестким значением от 0 до 1. При значении 0 смешивание отсутствует, и единственная текстура – это камень, а при значении 1 смешивание отсутствует там, где другая текстура имеет жесткий шов. Это дополняется параметром Level. Параметр Level должен быть установлен на Slider с минимальным значением, установленным на 0, а максимальным – на 100, а параметр Default – на 1; их можно установить в Node Settings в Graph Inspector. Мы добавили его в этот шейдер, чтобы показать, что вы можете добавить больше инструментов к каждому материалу для своих художников. В конце этой строки данных находится Saturate, гарантирующий, что данные придерживаются диапазона 0–1, который нам нужен для значения T Lerp.
Рис. 12.9. Текстуры и Lerp
Доработка ассетов 259 На рис. 12.9 показан наш Lerp. Base texture является значением, а B – текстура Y-up. T – результат нашего насыщения на рис. 12.8. Вывод Lerp переходит в наш базовый цвет. Это только начало, и вы можете усилить его, используя карты нормалей и карты высот, чтобы смешать каналы и сделать их еще более цельными. В настоящее время в этом шейдере мы не используем дополнительные карты, но концепция применяет точно такие же ноды, только с дополнительными картами в качестве входных данных.
Беспорядок в окружающей среде Эта работа сама по себе разумеющаяся. Те, кто работает с беспорядком в окружающей среде, известны в отрасли как clutter-художники. Их работа состоит в размещении предметов так, чтобы окружающая среда выглядела живой. В настоящее время у нас есть среда, спроектированная механически. Мы знаем, где должна быть Мивари, чтобы запускать ролики. Мы знаем, как она будет работать с задачами по физике. Чего мы не знаем, так это того, как люди жили в этом пространстве раньше. Для чего использовались эти пространства до того, как появились головоломки, открывающие двери? Вокруг должны быть чуть сломанные предметы или все уже давно разрушилось? Должна ли быть паутина или растения, растущие на некоторых деталях? Clutter-художники будут иметь набор мелких предметов, которые можно разместить вокруг, чтобы создать ощущение, что здесь что-то происходило в какой-то момент времени. Здесь у нас есть возможность рассказать маленькие истории в каждом разделе.
Детализация меша Рельеф Unity может содержать детализированные меши для размещения более простых мешей, таких как трава или небольшие камни. Мы кратко объяснили это в главе 5. Основная причина, по которой тема помещена в этой главе, состоит в том, чтобы объяснить, что с деталями предстоит еще поработать. Это очень похоже на работу clutter-художника; однако это относится не к тому, как жилось в этом пространстве, а к развитию природы. В нашем случае мы имеем ввиду траву и камни. Нам нужно убедиться, что трава и камни находятся в правильном месте. Это в первую очередь работа с более мелкими деталями очистки сцены.
Эффекты Полировка эффектов напоминает полировку анимации. Их нужно усовершенствовать, чтобы они стимулировали правильные эмоции зрителя. Большинство эффектов в этом вертикальном фрагменте должны быть фоновыми. Мы рассмотрим два из них. Первый будет блокировать лестницу в первой части пещеры. Вторым будет телекинез Мивари. Мы выбрали именно эти два эффекта для описания в книге, поскольку они совершенно отличаются друг от друга.
Блокировщик лестницы Блокировщик лестницы предназначен для того, чтобы создать препятствие для игрока при подъеме по лестнице. Игрокам нужно найти способ отключить блок,
260 Последние штрихи чтобы пройти дальше. Мы решили использовать что-то типа тайной энергии, движущейся вверх перед лестницей. Это будет сделано исключительно с помощью шейдера, поэтому рассмотрим некоторые простые приемы в Shader Graph. Изображение эффекта, показанное здесь, на рис. 12.10, статично, так что прыгайте в проект и посмотрите на первую область головоломки перед лестницей.
Рис. 12.10. Эффект блокировки лестницы
Такой эффект достигается за счет использования текстуры, состоящей из каналов с тремя уникальными текстурами облаков. В Adobe Photoshop текстуры облаков представляют собой шум Перлина в градациях серого. Мы взяли каждый слой и поместили его в красный, зеленый и синий каналы, чтобы получить три текстуры на одном изображении. Это позволяет нам использовать несколько разных облачных текстур для создания собственного рисунка шума при анимации его UV. Чтобы заставить эффект работать, нам нужен был способ анимировать эти UV несколькими способами. Мы выбрали наборы A и B, которые создали в наших параметрах. Давайте пройдемся по всем параметрам. Мы объясним, почему у нас есть каждый параметр, когда вырастем из этого эффекта, как показано на рис. 12.11.
Рис. 12.11. Параметры StairShield из Blackboard
Доработка ассетов 261 У нас есть Color, задающий общий цвет тайной магии. Cloud Tex будет текстурой, которую вы можете использовать для этого шейдера. Затем у нас есть Offset и Tiling с версиями A и B. Далее, есть два ребра, которые используются для ноды Smoothstep. Сначала нужно выяснить, как сделать текстуру анимированной. Мы будем использовать Tiling, Offset и Cloud Tex для выполнения этой начальной части шейдера. Глядя на рис. 12.12 ниже, мы ранее видели ноды Sample Texture 2D и Multiply. Нода Time дает вам доступ к игровому времени, sin и cos игрового времени, дельта-времени и сглаженной дельте. Мы будем использовать игровое время и умножать его на постоянное значение нашей скорости. Следующая новая нода – Tiling And Offset. Она является служебной нодой, помогающей справиться с тайлингом и смещением UV на сетке, к которой будет применяться материал. Мы присваиваем смещение Vector2 умножению времени. Это обеспечит движущееся значение для нашего смещения и анимирует UV в том направлении, в котором вы хотите. Последняя часть – подключить Tiling And Offset к UV-входу ноды Sample Texture 2D. Вы не видите B-наборы Offset и Tiling на этом изображении, так как это одни и те же ноды с разными параметрами. Для того чтобы иметь независимые текстуры с разными скоростями и масштабами тайлинга UV, нужно несколько наборов. Это создает динамическую текстуру на выходе.
Рис. 12.12. Offset и Tiling для нашей облачной текстуры
Нам нужно собрать, казалось бы, бесконечный мозаичный узор. Все эти шумовые паттерны мозаичны как в горизонтальном, так и в вертикальном направлениях. Иногда это называют мозаичной текстурой с четырьмя путями. Мы планировали сместить Offset А вверх по оси Y на большую величину, а затем Offset В немного поменьше. Мы также замостили бы набор B где-то между .5 и .75. Это дало бы нам совершенно другой набор шумов для наложения поверх другого.
262 Последние штрихи
Рис. 12.13. Пересечение каналов
На рис. 12.13 мы создаем три динамических изображения, которые нужно объединить. Обе ноды Sample Texture 2D имеют разные настройки тайлинга и разные смещения, перемещающиеся во времени. Соединение их вместе с умножением неизбежно создаст живую облачную структуру, когда они пересекаются. Мы делаем это со всеми тремя каналами (R, G, B). Затем умножим каждый из них на 5, чтобы все каналы изображения были поверх их оригинала. Далее объединяем три канала в один выход, добавляя первые две перемноженные ноды, а затем еще одну, как показано ниже на рис. 12.14. Теперь, когда у нас есть единый поток данных с движением, можем добавлять значения, чтобы добиться более интересного эффекта. Здесь нам нравится применять Smoothstep. Это позволяет многоуровневым данным создавать интересные формы, как показано на рис. 12.15. Проблема заключается в том, что в этом процессе теряется общая облачность, поэтому мы хотим добавить предыдущий Add, а затем насытить его, чтобы значение точно находилось в диапазоне 0–1, после чего умножить его на параметр цвета, чтобы мы могли изменить цвет в инспекторе. Выход ноды Color перейдет в base color. Затем мы создаем материал, использующий шейдер SH_StairShield, и применяем его к плоскости в сцене, где мы хотели показать, что что-то блокирует лестницу.
Доработка ассетов 263
Рис. 12.14. Multiply и Add
Рис. 12.15. Smoothstep и Color
Система Shuriken – блокирующий слой частиц на лестнице Нам нравится, как выглядит блокировщик лестницы, но для эффектов нужны слои, чтобы они выглядели как хорошо сделанные произведения искусства. Нам также нужно было потратить немного времени на изучение самого Shuriken. Этот эффект будет проходить через некоторые основные части Shuriken для создания простых эффектов, которые можно наложить на ваш мир. То, что мы будем создавать, – это растянутый спрайт, движущийся вверх, чтобы дать больше энергии блокировщику лестницы. Для начала мы хотели сделать что-то с элементом по умолчанию, чтобы показать силу систем частиц. Мы используем материал ParticlesUnlit, представляющий собой простой радиальный градиент от центра. Иногда мы на-
264 Последние штрихи зываем их «математическими точками», так как их можно создать без текстуры. Мы хотим создавать частицы, которые имеют эффект энергии и движутся вверх, но замедляются ближе к концу своей жизни и исчезают. Их настройки рассмотрим ниже; тем не менее рекомендуем вам самостоятельно посмотреть в проекте систему частиц и поиграть с настройками. Внесите некоторые изменения и проверьте, можете ли вы сделать, по вашему мнению, лучше. Поделитесь результатом на Discord! Система Shuriken имеет большое количество параметров внутри модулей. Рассмотрим только те, которые мы модифицировали и которые необходимы для нашей простой системы. Мы просим вас просмотреть документацию Unity для объяснения всех параметров и модулей. Давайте сначала посмотрим на основной модуль, показанный ниже на рис. 12.16.
Рис. 12.16. Главный модуль Shuriken
Единственными параметрами, которые мы изменили здесь, были Start Lifetime, исправленный на значение 1.4, и Start Speed, установленный на 0. Мы изменили время жизни после внесения всех остальных исправлений, так как не знали точно, как долго должна жить система частиц. Начальную скорость мы установили на 0, потому что знали, что хотим контролировать скорость. Также изменили цвет, но позже переопределим его в модуле Color Over Life. Следующий модуль, который рассмотрим, – это Emission. Как видно выше на рис. 12.17, это модуль Emission. Мы изменили Rate over Time на 30, чтобы у нас точно появлялось много частиц. Эмиссия ваших частиц сильно зависит от того, что вам нужно передать. Что касается нас, мы хотели иметь достаточно сильную эмиссию, чтобы добавить к шейдеру барьера лестницы, но не слишком много, дабы не перегрузить его.
Доработка ассетов 265
Рис. 12.17. Модуль Emission
Теперь у нас есть куча появляющихся частиц, но мы знаем, что хотим, чтобы они сначала появлялись в нижней части блокировщика лестницы. Будем использовать модуль Shape, чтобы ограничить места появления, что имеет смысл для цели эффекта.
Рис. 12.18. Модуль Shape и фигура в игре
Мы выбрали форму куба, так как хотели, чтобы частицы появлялись из нижней части блокировщика лестницы и двигались оттуда вверх, следуя потоку движения. Затем нужно было заставить эти частицы двигаться. Мы знаем, что хотели, чтобы они двигались быстро вверх, поэтому установили 100 в Linear Z, как показано на рис. 12.19. Это отбрасывает их в космос, поэтому стоит добавить компонент сопротивления к нашей скорости, чтобы замедлить их ближе к вершине. Это происходит из-за ограничения Velocity over Lifetime.
266 Последние штрихи
Рис. 12.19. Модуль Velocity over Lifetime
На рис. 12.20 показано, где мы добавим сопротивление нашим частицам. Сопротивление сохраняем на постоянном уровне и устанавливаем его на 5. Заметьте, это значение дало хорошее сопротивление. Мы не были уверены в нем изначально; мы просто играем со значениями, пока не почувствуем, что это именно то, что ищем.
Рис. 12.20. Модуль Limit Velocity over Lifetime
Затем нам нужно раскрасить эти частицы, поскольку они представляют собой просто белые математические точки, идущие вверх. Включение модуля Color over Lifetime, показанное ниже на рис. 12.21, позволяет определить градиент, где левая сторона – это начало жизни частицы, а правая сторона – конец жизни частицы, включая альфа-канал частицы, если ваш материал настроен на прием альфы.
Рис. 12.21. Color over Lifetime
При нажатии на градиент откроется редактор градиентов, показанный ниже на рис. 12.22. Верхняя часть градиента – это альфа, а нижняя – цвет. Попробуйте изменить цвет на них, чтобы увидеть, как он меняет частицы! Теперь мы устанавливаем режим рендеринга из модуля Renderer. Поскольку мы знали, что хотим, чтобы частицы с самого начала растягивались от скорости, мы заранее изменили эту настройку на Stretched Billboard. Если бы вы следовали нашим действиям по созданию частиц, ваши частицы выглядели бы как цветные точки, а не полосы. Изменение Render Mode на Stretched Billboard исправит это. Мы также установили Speed Scale на 0.1, так как они двигаются очень быстро, что заставляет их растягиваться очень длинными, если вы выберете значение намного выше 0.1.
Доработка ассетов 267
Рис. 12.22. Gradient Editor
Рис. 12.23. Модуль Renderer
Мы только что показали простой пример из доступных систем растянутой частицы. Гораздо эффектнее, когда вы добавляете шейдеры к частицам. Хорошо продуманный визуальный эффект может вызвать эмоции от происходящего действия. Хотя поначалу это может показаться пугающим, если вы разберете то, что вам нужно, игра с качественными настройками станет более увлекательной. Вдобавок, когда войдете в проект, увидите другие эффекты Shuriken на уровне. Не стесняйтесь поразбирать их, чтобы узнать о различиях в настройках и о том, какие они играют роли в визуальных эффектах. В следующем разделе рассмотрим VFX Graph. Это еще один создатель системы частиц, который позволяет нам создавать частицы графического процессора. Способ работы отличается, поскольку он имеет собственный дизайн системы и пользовательский интерфейс за пределами инспектора. Давайте рассмотрим пример, который мы используем в проекте.
VFX Graph – телекинез Мивари Телекинез может выглядеть как угодно. Наша цель в том, чтобы игрок чувствовал, будто Мивари использует священную энергию, текущую от нее к объекту, которым она управляет. В этой части мы расскажем, как настроить весь VFX Graph, шейдер и немного кода для реализации. Мы предполагаем, что у вас уже установлен пакет VFX Graph и вы открыли FX_BeamSetup Visual Effect Asset. Контекст Spawn запускается по умолчанию со значением частоты появления Constant. Нужно просто запустить один раз 32 частицы, которыми мы будем манипулировать, пока блок работает. Мы удалили постоянный спавн и вместо этого вставили блок Single Burst, как показано на рис. 12.24 ниже.
268 Последние штрихи
Рис. 12.24. Контекст Spawn
Число 32 с самого начала не было точным. Мы не были уверены, сколько частиц нам понадобится, но достаточно легко добавить больше в процессе создания полосы. Ниже на рис. 12.25 показан контекст Initialize. Нам нужно установить Particle Per Strip Count на то же число, что и спавн во вспышке выше. Далее – блок Set Size и блок Set Custom Attribute. Этот блок атрибутов будет иметь тип данных с плавающей точкой, и мы назвали его InterpolatedPosition. Причина для такого названия заключается в том, чтобы иметь индекс каждой частицы и мы могли по отдельности размещать их там, где хотим.
Рис. 12.25. Контекст Initialize
Доработка ассетов 269 На рис. 12.26 видно, что мы получаем индекс частиц, а затем делим его на единицу меньше, чем общее количество, – 31. Индекс начинается с 0, поэтому нам нужно начать с числа на единицу меньше, чем то, которое мы спавним. Это дает значение, с которым можно работать, и оно сохраняется в созданном нами кастомном атрибуте float.
Рис. 12.26. Ноды Particle Index
Теперь у нас есть полоса частиц, у которой должна быть позиция для перехода. Создадим два параметра преобразования на доске так же, как мы делаем это в Shader Graph. Мы назвали их BeamStart и BeamEnd. Положение частиц изменим от начала до конца луча в соответствии с интерполированным плавающим положением, с которым мы инициализировали. На рис. 12.27 вы увидите, как мы соединяем их вместе. Выходные данные Lerp перейдут в контекст Update.
Рис. 12.27. Позиционирование луча
В контексте обновления есть два блока, как показано ниже на рис. 12.28: Set Position и Add Position. Добавим вывод Lerp для их позиции в этот блок. Есть один трюк, который заставит произойти некоторое необычное движение. В блоке Set Position есть маленькая буква w посередине. Если это L, тогда блок перемещает локальную позицию. Это вызовет двойное преобразование при перемещении игровых объектов. Если вы нажмете на L, она изменится на w,
270 Последние штрихи что означает мировое пространство. Можно оставить Add Position в локальном пространстве.
Рис. 12.28. Контекст Update
В настоящее время у нас есть прямой луч от начала до конца. Подходит для тестирования, но нам нужно что-то повеселее. Давайте добавим немного турбулентности, чтобы он не был таким жестким. Мы будем использовать блок Add Position, и входными данными для него будут некоторые манипуляции с 3D-шумом. У него есть еще несколько нод, чтобы получить правильные данные для хорошей турбулентности. Глядя на рис. 12.29 ниже, мы видим, что эти пять нод выполняют задачу. Мы хотим получить нашу текущую позицию, а затем прибавить ее ко времени. У нас есть нода Multiply между ними, чтобы можно было ускорить или замедлить значение времени. Это также может быть настраиваемая переменная. После Add идет Perlin Noise 3D. Значения здесь чисто субъективны. Поместите свои координаты в слот Coordinate, а затем производные выходные данные – во вход блока Add Position в контексте Update. Поиграйте со значениями, пока не получите желаемую турбулентность. Однако с данным подходом есть проблема. Вы обновите каждую частицу, включая начало и конец луча. Это кажется странным, поскольку мы хотели, чтобы это исходило от руки нашего персонажа.
Рис. 12.29. 3D Perlin Noise
Чтобы начало и конец луча не зависели от этого, мы использовали простой градиент, чтобы указать положению, должно ли оно использовать турбулент-
Доработка ассетов 271 ность. По рис. 12.30 видно, что мы берем интерполированное значение положения и семплируем его по этой интерполяции с течением времени. Градиент теперь действует как передача, на которую будет воздействовать частица. Значение 0 в начале и в конце полосы даст значения 0, умноженные на производные от генератора шума. Теперь мы подключаем это к блоку Add Position.
Рис. 12.30. Маска для турбулентности
Мы уже находимся на финишной прямой для настройки части VFX Graph. Контекст показан на рис. 12.31. По умолчанию это будет Output Particle Quad. Но он не принесет нам никакой пользы, поэтому удалите его, если он есть на вашем VFX Graph, и нажмите пробел, чтобы создать новую ноду. Затем введите particlestrip. Найдите Output ParticleStrip Quad. Тот, что ниже, не светится в названии; это связано с используемым материалом.
Рис. 12.31. Контекст Output ParticleStrip Quad
Шейдер является дубликатом SH_StairShield с одним изменением. В инспекторе Graph для логического значения Support VFX Graph установлено значение true. Этот шейдер обладает достаточной универсальностью, чтобы выполнять свою работу на данный момент. Мы можем отредактировать текстуру перед ее окончательным использованием, но сейчас у нее есть то, что нам нужно, чтобы заставить ее работать. Затем мы назначаем его атрибуту Shadergraph в выходном контексте. Это выставит параметры в шейдере.
272 Последние штрихи Есть еще два шага, чтобы завершить данный эффект. Нам нужно создать начало и конец луча GameObject, а затем реализовать этот эффект, размещая местоположения GameObjects во время игры. Для начала создадим префаб. На рис. 12.32 мы создали пустой GameObject и назвали его Telekinesis. Затем поместили объект настройки луча в качестве дочернего и установили его положение на 0, 0, 0. Затем мы создали еще два пустых игровых объекта и назвали их BeamStart и BeamEnd. Также устанавливаем их положения на 0, 0, 0.
Рис. 12.32. Префаб Telekinesis
Существует компонент, который вы можете добавить к ассетам VFX Graph, он называется VFX Property Binder. Добавьте этот компонент в игровой объект FX_ BeamSetup. Затем создаем два связанных свойства для преобразования и называем их так же, как свойства в графике VFX Graph (BeamStart и BeamEnd). Перетащите GameObject в слот Target, чтобы сослаться на преобразование GameObject. Сделайте то же самое для BeamEnd. Это будет выглядеть так, как показано на рис. 12.33.
Рис. 12.33. Компонент VFX Property Binder
Теперь нам нужно перейти к реализации. Смысл здесь заключаются в том, что начало луча должно исходить от левой руки персонажа. Мы также знаем, что нужно, чтобы конец луча был прикреплен к предмету, которым мы управляем с помощью физики. Также нужно включать и выключать визуальный эффект только тогда, когда кнопка взаимодействия взаимодействует с элементом физической головоломки. Мы будем работать с DragRigidBody.cs. Данный скрипт использует центр экрана в качестве точки отсчета, и, если вы находитесь в пределах досягаемости физического элемента, с которым можете взаимодействовать, он даст Мивари контроль над этим Rigidbody с помощью скриптов физических кусочков головоломки, которые мы рассмотрели в главе 6.
Доработка ассетов 273 Добавить в код: public public public public
VisualEffect telekinesis; Transform leftWristLoc; Transform beamStart; Transform beamEnd;
Они будут назначены в редакторе и должны говорить сами за себя, за исключением, возможно, leftWristLoc. Это преобразование происходит от суставов Мивари в ее иерархии. Разверните ее иерархию и перетащите левое запястье на этот слот в инспекторе. В обновлении мы хотим отключить луч, если отпустить кнопку взаимодействия. if (control.wasReleasedThisFrame) { // Освободить выбранное Rigidbody, если оно есть selectedRigidbody = null; telekinesis.enabled = false; }
После этого нужно поработать с файлом FixedUpdate. Мы работаем с физикой, поэтому следует попросить программу проверить, есть ли у нас Rigidbody, и на FixedUpdate мы включим луч, если это правда, и установим положения beamStart и beamEnd в каждом цикле FixedUpdate с физикой. if (selectedRigidbody) { telekinesis.enabled = true; ... beamStart.position = leftWristLoc.position; beamEnd.position = selectedRigidbody.gameObject.transform.position; }
Вот и все! Сохраните файлы, вернитесь в редактор и назначьте преобразования и визуальные эффекты скрипту, который находится в Main Camera. На рис. 12.34 ниже показан выбранный объект со скриптом.
Рис. 12.34. Расположение основной камеры со скриптами телекинеза
274 Последние штрихи Эффекты частиц и работа с шейдерами предполагают интересные проблемы, к которым нужно относиться с осторожностью. Слишком много хороших вещей заканчиваются головной болью. Проходя уровень, найдите минутку, чтобы подумать о мелких деталях и понять, имеет ли смысл нагружать небольшими движениями, чтобы продать опыт. Из приведенных выше двух эффектов каждый визуальный эффект продуман довольно тщательно независимо от его размера. Внимательно и без спешки просмотрите каждый эффект в игре.
Синематики В нашем проекте мы используем синематики для трех целей. Во-первых, нужно объяснить, что эта местность существует уже давно, поэтому все вокруг хрупкое. Во-вторых, показать игроку, что Мивари обладает врожденными способностями, защищаясь от падающего валуна. Третий синематик – финальная сцена, когда она надевает свою тиару и проходит через портал после завершения последней головоломки. Суть работы с синематиками заключается в том, что мы экспортируем модели, пока они находятся в окружающей среде. Таким образом, синематики соответствуют окружающей среде с максимально возможной точностью.
Вторичная анимация Иногда требуется дополнительная анимация, которую легче имитировать, чем настраивать вручную. Волосы являются хорошим примером этого. Действия, которые совершают волосы, являются вторичной анимацией. Конечно, это возможно сделать вручную, но потребуется огромное терпение, а вместо этого можно прибегнуть к помощи физики. Для этого будем использовать компонент Unity Spring Joint. В магазине ассетов Unity также есть несколько ассетов, которые были созданы, чтобы сделать этот процесс более приятным. Если вам нужна простая физика для вторичной анимации, это можно сделать с помощью физического компонента Unity Rigidbody, компонента Spring Joint и капсульных коллайдеров.
Освещение Мы решили внести последние штрихи в освещение, а по этой теме могла бы быть отдельная книга. Это одна из тех тем, которые представляют собой огромную кроличью нору. Мы хотели рассмотреть здесь некоторые основы освещения и причину, по которой важно уделять внимание освещению, а также выделить несколько инструментов полировки и способы использования освещения в Unity. Во-первых, нужно заметить, что освещение – это искусство. Цель освещения включает в себя определение 3D-формы, создание настроения и методы разработки игрового процесса. Рассмотрев несколько дизайнерских мыслей об освещении, мы совершим экскурсию по смешанному освещению Unity, картам освещения, отражениям и световым зондам.
3D-форма Без освещения трехмерная форма является плоской. На самом деле для большинства эффектов мы используем неосвещенные шейдеры. Одна из причин
Освещение 275 заключается в том, что нам не нужно добавлять тени и освещение для небольших блестящих эффектов, которые будут отображаться на экране только в течение короткого времени. Они плоские и не нуждаются в освещении, чтобы выделить их; это делает их форма текстуры.
Обеспечение настроения Идет рука об руку с дизайном окружающей среды, но особое внимание уделяется освещению. Становится ли переулок темнее, когда вы идете по нему? Это может вызвать у игрока чувство опасности или встревоженности. Вы хотите, чтобы неестественные цвета освещения вокруг определенных мест создавали таинственное ощущение внутри дома мага? Вполне возможно! Все эти решения следует учитывать при размещении освещения. В том же духе, что и настроение, мы могли бы захотеть, чтобы наши источники света определяли игровой процесс.
Дизайн гейм-плея Гейм-плей можно определить через освещение разными способами. На самом деле вся ваша игра может быть построена вокруг света. В играх ужасов часто используются источники света, чтобы оттолкнуть врагов, но это ограничено небольшим таймером, поскольку ваши батареи в фонариках неизбежно садятся! Выбирая уникальный маршрут, старая игра под названием Boktai использовала периферийный датчик света для Game Boy, заряжая ваши оружия, и, если вы играли в нее в темноте, игра была более сложной. Эти концепции элементов гейм-плея немного на грани. Мы могли бы просто использовать свет, чтобы дать игроку представление о том, куда идти, а от чего держаться подальше. Вероятно, теперь у нас есть хорошее представление об общих концепциях дизайна освещения и о том, как это может повлиять на впечатления игрока. Давайте углубимся в освещение Unity.
Освещение Unity Чтобы добраться до идеального состояния, нам нужно сначала пройтись по основам. Это будет обзор того, что вы можете сделать для освещения в Unity, а затем мы рассмотрим, какие настройки и способы использования у нас есть для нашего проекта. Встроенный рендерер, освещение URP и HDRP отличаются друг от друга. Мы будем говорить конкретно об освещении URP. Также будем продвигать определенное ощущение и объяснять функции, помогшие добиться желаемого вида, к которому мы стремились в нашем вертикальном срезе. Каждый ассет освещения можно настроить по-разному, а это означает, что описанные ниже шаги окажут ровно столько помощи, сколько необходимо, чтобы начать работу с освещением. После того как вы пройдете через это и поэкспериментируете с тем, что мы объясним, мы настоятельно рекомендуем прочитать документацию по другим объектам освещения для различных конвейеров рендеринга в зависимости от потребностей вашего проекта. Теперь, когда мы рассмотрели конструкцию освещения, начнем с разговора о смешанном освещении.
276 Последние штрихи
Смешанное освещение Здесь мы немного срезаем путь, с самого начала переходя к смешанному освещению. Чтобы правильно его использовать, вам нужно использовать непрямое запеченное и динамическое освещение. Мы коснемся обоих прямо сейчас, а затем вернемся к смешанному освещению. Непрямое запеченное освещение Освещение в реальном времени, отбрасывающее световые лучи на статические игровые объекты, отражающиеся от геометрии в мире, будет запекаться на карте освещения. Эти термины новые! Статические игровые объекты определяются установкой флажка Static в инспекторе, как показано на рис. 12.35 ниже.
Рис. 12.35. Флажок Static
После установки флажка, когда игра запекает свои карты освещения в Lightmap UV, она будет знать, что нужно добавить это к элементам для запекания. Вы бы выбрали это как статическое только в том случае, если точно знаете, что никогда не будете перемещать GameObject. Мы совершенно уверены, что этот бетонный забор останется неподвижным на протяжении всей игры, поэтому выбрали его как статический. Следующий термин – карта освещения (Lightmap). Это вторичный набор UV, которым не разрешено перекрываться с объектом, на который вы хотите запечь освещение. Когда вы импортируете модель, можете позволить Unity сгенерировать UV-карты освещения для вас, и она отлично с этим справится. Сделать это можно, выбрав FBX для 3D-модели и Generate Lightmap UVs, как показано на рис. 12.36. Когда вы установите флажок, отобразятся настройки Lightmap UV. Эти значения являются средними для каждого объекта в вашей сцене. Данные настройки отлично справятся с основами, но вам может потребоваться изучить каждый атрибут, чтобы быть уверенным в правильности освещения каждого объекта. Эти настройки для объектов, которые получают свет. Что касается источников света, вы можете установить любой доступный источник света в качестве запеченного. Направленный, точечный, рассеянный и зональный свет доступны для добавления в карты освещения при создании или запекании освещения.
Освещение 277
Рис. 12.36. Опция Generate Lightmap UVs
Динамическое освещение Иначе можно назвать освещением в реальном времени. Освещение в реальном времени должно иметь дело с тенями в реальном времени и многими другими настройками, связанными с этим. Освещение в реальном времени применяется к любому элементу, который не был выбран в качестве статического. Скелетные меши всегда в реальном времени, поскольку они не могут быть статичными. Их природа – двигаться! В нашем ассете URP мы видим, что в настройках Shadows можно установить расстояние, на котором снижается качество теней. Ниже на рис. 12.37 вы можете увидеть этот диапазон в разделе Shadows.
Рис. 12.37. Настройки URP Shadows
278 Последние штрихи Каждый источник света в реальном времени будет использовать такие настройки для теней. Cascade – это во сколько раз снижается качество света от расстояния. По умолчанию значения в метрах. Это может помочь нам спроектировать пределы, поскольку мы знаем, какого роста должны быть наши персонажи в целом. Одна единица Unity по умолчанию равна одному метру. Вы можете настроить тестовую сцену и посмотреть, как будут выглядеть тени на расстоянии каждого каскада. Из уникального в среде Unity для источников света в реальном времени – это четыре доступных источника света. Направленный, точечный, рассеянный и зональный свет доступны для получения информации об освещении в реальном времени. Зональные источники света не могут создавать тени в реальном времени. Теперь, когда мы рассмотрели основы работы с непрямым освещением в реальном времени, вернемся в режим смешанного освещения. Во-первых, нам нужно, чтобы вы знали, как разместить свет на сцене. На рис. 12.38 вы можете увидеть список источников света. Получить доступ к этому меню можно так же, как вы создаете любой GameObject, щелкнув правой кнопкой мыши в иерархии или перейдя в меню GameObject и наведя курсор на параметр Light, чтобы открыть подменю, показанное на рис. 12.38.
Рис. 12.38. Параметры освещения
Мы говорили об обоих режимах освещения. В некоторых играх может использоваться только запеченное освещение, а в некоторых – только освещение в реальном времени. Большинство игр будут использовать оба варианта в URP. Когда вы выбираете любой созданный вами источник света, в инспекторе есть возможность выбрать режим реального времени, смешанный или запеченный. Помните, запеченный означает запеченный непрямой свет. Лучшая часть смешанного режима заключается в том, что он позволяет запекать свет там, где он есть, но действует как динамический при добавлении в нестатический GameObject. Это полезно для направленного света. Такой свет действует как солнце, поэтому мы хотим, чтобы он запекался для статических предметов, но был динамическим для персонажа или чего-либо нестатического. Посмотрите, как это выбрано в инспекторе на рис. 12.39. Даже после того, как вы установили все необходимые меши в статическое состояние, разместили источники света и настроили их на режим реального времени, запеченные или смешанные, вам все равно нужно настроить параметры освещения в окне освещения. Чтобы попасть туда, используйте снимок экрана ниже на рис. 12.40.
Освещение 279
Рис. 12.39. Направленный свет установлен на смешанный
Рис. 12.40. Путь к окну Lighting
В появившемся окне у вас будет несколько настраиваемых параметров. Эти настройки будут уникальными для каждого проекта. Мы знаем, что нам важна хорошая точность воспроизведения теней. Это означает, что нужно больше семплов и более высокое разрешение для карт освещения. Мы также собираемся быть довольно близко к персонажу в игре и во время синематиков, которые все еще происходят в реальном времени. Эти факторы необходимо учитывать при выборе настроек. Вы могли бы изменить настройки и получить хорошие тени с огромным запеканием света, но тогда ваши тени в реальном времени могут не справиться с этим и будут блочными, из-за чего игра будет создавать странное ощущение. Рассмотрите систему, на которой будет ваша игра, и снова тщательно протестируйте производительность после добавления большего количества источников света и карт освещения. Есть еще один инструмент, который можно использовать для получения более точной информации об освещении в реальном времени внутри Unity без
280 Последние штрихи необходимости иметь много источников света в реальном времени. Это называется световые зонды. Давайте посмотрим на этот инструмент.
Световые зонды Создать световые зонды просто – перейти в GameObject группу Light и выбрать Light Probe Group. Этот инструмент делает выборку информации об освещении в трехмерных точках, показанных на рис. 12.42. Затем эта информация используется в режиме реального времени, даже если освещение представляет собой только запеченную информацию. Это очень полезно, если вы хотите использовать окраску из области освещения (которая только запекается) и добавить ее к персонажу. Подумайте о свете на стене, где вам не нужно отбрасывать тень или чтобы он был в реальном времени. Вместо того чтобы тратить много ресурсов, можете просто использовать световые зонды вокруг этой области, и это поможет уловить нестатическую геометрию в реальном времени. Однако, чтобы настроить это, необходимо разместить световые зонды вручную. В Asset Store есть ассеты, которые можно автоматически размещать, но имейте в виду, что все, что автоматизировано в индустрии развлечений, требует участия художника, чтобы достичь именно того, что нужно. Light Probe Group при редактировании группы в инспекторе выглядит так, как показано на рис. 12.41 ниже.
Рис. 12.41. Компонент Light Probe Group в инспекторе
Вы можете добавлять, удалять, выбирать все и дублировать выбранные. Когда вы размещаете световые зонды, просто знайте, что они являются средними значениями нескольких цветовых местоположений. Это не идеальное представление света в одной области, а скорее приблизительное значение, чтобы дать немного дополнительного импульса и гарантировать сохранение настроения для персонажей в реальном времени в игре. При всем этом добавляйте зонды, пока они не образуют красивую решетку. Однако чем их больше, тем больше вычислительной мощности потребуется. Для каждого проекта, как обычно, в зависимости от системы будет разрешено для производительности свое количество световых зондов. После того как вы разместили их, вы можете либо нажать кнопку воспроизведения и пройтись, либо просто перетащить свои нестатические игровые объекты по сцене, чтобы увидеть небольшое смещение освещения. Вот пример начального коридора решетки светового зонда нашего вертикального среза на рис. 12.42.
Освещение 281
Рис. 12.42. Решетка светового зонда
Это может занять некоторое время. Если вы измените конфигурацию освещения, не забудьте переосмыслить и световые зонды после этого. Осталось еще одно дельце, прежде чем мы приступим к полировке звука. Мы хотим перейти к отражениям.
Зонд отражения В мире существуют материалы, которые отражают цвет окружающей среды. Это металлические и/или глянцевые материалы. А что же они отражают? Я рад, что вы спросили об этом, потому что Unity изначально создаст карту отражений только для скайбокса, чтобы в этих материалах что-то отражалось. Есть еще один инструмент, который вы можете добавить к своей сцене, – это датчик отражения, он позволит вам указать объем, содержащий данные отражения в этой области. Вы также можете иметь перекрывающиеся объемы. Интересный вопрос, ведь это не идеальное представление, поскольку положение отражения зонда находится в центре положения этого зонда. Если у вас есть большая площадь и нужно быть очень близко к отражениям, а также нужно, чтобы это отражение было точным, вам потребуется несколько датчиков отражения, причем объем каждого датчика должен быть таким большим, как вам нужно. Чем меньше объем, тем четче изображение отражения. Такие вещи не будут очень ясны, пока вы не побегаете по миру и не начнете искать отражающие поверхности или не проработаете синематики своей игры и не увидите странное отражение. Здесь есть небольшая оговорка; можете создавать отражения в реальном времени, но они очень затратны. Их следует использовать с осторожностью. До тех пор, пока у всех нас дома не появятся квантовые компьютеры. Чтобы создать зонд отражения, эта опция находится там же, где и все остальное освещение, в меню GameObject в разделе Lighting. Когда вы создадите зонд и разместите его в том месте, вокруг которого хотите отразить, вам придется использовать инспектор для редактирования объема, который выглядит так, как показано на рис. 12.43.
282 Последние штрихи
Рис. 12.43. Компонент Reflection Probe в инспекторе
Два значка вверху по центру предназначены для редактирования и перемещения объема. Выбор значка трех точек дает вам доступ к форме объема, чтобы уменьшать и увеличивать его в соответствии с вашими потребностями. Тип может быть Baked, Real-time или Custom. Baked запекается только один раз и не может измениться во время выполнения. Изменение Real-time происходит по мере того, как игра запускается в каждом кадре. Custom позволяет вам разместить свою собственную кубическую текстуру вместо семплирования среды. Это может быть полезно, если вы хотите исказить окружающую среду в отражениях! Настройки кубической текстуры предназначены для настройки масштаба и параметров для повышения необходимой точности за счет снижения производительности. Одним из наиболее важных параметров является параметр Importance! Этот параметр представляет собой целое число, которое вы устанавливаете, чтобы сообщить игре, какой зонд отражения отображается при наличии перекрывающихся объемов отражения. Работает следующим образом: чем выше число, тем выше важность. Если у вас есть два перекрывающихся объема, например внутри входа в пещеру, а не снаружи, вы должны установить коридор на уровень важности 2. Таким образом, когда вы входите в более важный объем, датчик отражения переключается. Это может вызвать некоторые деформации на очень близких отражающих поверхностях. Играйте в свою игру и обращайте внимание на отражения, когда они меняются. Добавление общего освещения – увлекательная задача. Это может значительно улучшить графическое качество вашей игры, и есть несколько отличных трюков, чтобы настроить это. Теперь можем перейти на доработку звука.
Доработка звука Чтобы сделать звуки в нашей игре более правдоподобными, у нас есть несколько способов. Доработка звука сводится к настройке громкости звуков, изменению минимального и максимального расстояния затухания и даже замене звуков, которые, по вашему мнению, звучат не очень хорошо.
Доработка звука 283 Это все вещи, уже настроенные ранее. Например, в одной из наших первых областей окружающей среды мы можем отрегулировать громкость или высоту тона. Или можем изменить минимальное или максимальное расстояние на затухание, добавить звуки, которые мы могли пропустить, сделать так, чтобы одни более важные звуки были громче других и т. д. В целом микширование и полировка звука – это очень итеративный процесс, в котором просто манипулируют значениями и заменяют одни звуки другими, чтобы подобрать лучший вариант. Вы никогда не знаете, как звук будет сочетаться с остальными звуками, пока не поместите его в игру.
Триггер звука через события анимации Мы хотели показать вам, как добавить звук к событию анимации. Это довольно простой процесс, поскольку мы уже знаем, как добавлять события анимации и как запускать звуки с помощью компонентов AudioSource. Будем добавлять звуки шагов к действию ходьбы нашего персонажа. Сначала давайте выберем нашего персонажа, MyvariWithCameraRig:
Рис. 12.44. MyvariWithCameraRig
Затем давайте перейдем к его дочерним объектам, чтобы найти игровой объект SM_Myvari. Здесь вы увидите компонент аниматора! Нужно сделать всего несколько вещей. Во-первых, давайте создадим новый скрипт и назовем его AnimationSounds, затем поместим его прямо под нашим Animator Component. После этого добавим компонент AudioSource. Все это должно выглядеть примерно так, как показано на рис. 12.45. Прежде чем продолжить, давайте добавим функцию в наш скрипт AnimationSounds. Удалите функции Start и Update и добавьте новую функцию PlaySound(). Над этой новой функцией объявите новую общедоступную переменную с именем public AudioSourceAnimSound.
284 Последние штрихи
Рис. 12.45. Окно инспектора SM_Myvari
Рис. 12.46. Наш новый AnimationSounds.cs
Доработка звука 285 Теперь внутри функции PlaySound() давайте добавим AnimSound.Play(). Далее в инспекторе мы можем добавить компонент AudioSource в сериализованное поле в компоненте AnimationSounds.cs и добавить звуковой эффект шагов!
Рис. 12.47. Скрипт AnimationSounds.cs в инспекторе
Отлично! Теперь можем перейти к маркировке нашей анимации событиями.
Маркировка анимации событиями для звука Одна большая проблема, связанная с добавлением событий анимации, заключается в том, что мы не можем добавлять события непосредственно через окно анимации, поэтому придется открывать файл FBX внутри Unity. Лучший способ сделать это – перейти в Assets > Animations и выбрать Myvari_Walk_Basic FBX.
Рис. 12.48. Проводник проекта в Unity в папке Assets > Animations
Затем мы будем прокручивать инспектор вниз, пока не дойдем до раскрывающегося списка Events. Откройте раскрывающийся список Events, а также окно Preview в нижней части инспектора. Оно может быть скрыто в нижней части инспектора, но вы можете щелкнуть и перетащить снизу, чтобы открыть его! Это должно выглядеть примерно так, как показано на рис. 12.50.
286 Последние штрихи
Рис. 12.49. Окно инспектора анимационных клипов
Доработка звука 287
Рис. 12.50. Предварительный просмотр окна Animation Clip
Затем, используя временную шкалу над превью, можно переходить к различным частям анимации. В частности, мы пытаемся найти места шагов, т. е. такие места, где ступня касается земли:
Рис. 12.51. Предварительный просмотр окна Animation Clip
288 Последние штрихи Как только ваша временная шкала будет выстроена, добавьте событие анимации. А в месте с надписью Function введите PlaySound – не включайте скобки, которые вы видели ранее (в PlaySound())! По какой-то причине включение круглых скобок не запустит нашу функцию должным образом. Здесь мы разместили наши события.
Рис. 12.52. Временная шкала с событиями окна Animation Clip
Теперь, когда вы зайдете в игру и прогуляетесь, вы услышите звук! Поздравляю! Теперь у нас есть звуки шагов!
Рандомизированные звуки Вы, вероятно, заметите, что звук шагов довольно повторяющийся. Вот почему нам часто нравится добавлять в игру рандомизированные звуки! Это процесс случайного воспроизведения из пула звуковых эффектов, чтобы звуки были менее повторяющимися! В этом случае у нас есть пять различных звуковых эффектов шагов на выбор, которые можно найти в /Assets/Sounds: MainFS_01.wav – MainFS_05.wav
Затем давайте откроем скрипт AnimationSounds.cs и посмотрим, как мы можем добавить рандомизированные звуки. Итак, в этом случае мы собираемся использовать список AudioClips, как здесь:
Рис. 12.53. Animation.cs публичный список soundPool
Далее внутри PlaySound мы выберем случайный клип и загрузим его в наш компонент AudioSource. Для этого будем использовать Random.Range:
Рис. 12.54. Функция Animation.cs PlaySound()
Доработка звука 289 Затем давайте откроем инспектор, где находится наш скрипт AnimationSounds.cs, выделим все наши звуки MainFS.wav, щелкнем и перетащим их прямо в сериализованное поле нашего звукового пула:
Рис. 12.55. Animation.cs в инспекторе
Готово! Теперь играем из набора случайных звуков!
Рандомизированная тональность Иногда добавление еще большего количества вариаций может быть достигнуто путем рандомизации высоты тона. Это также очень простой процесс. Первое, что нужно сделать, – это определить диапазон высоты тона, на который мы будем воздействовать. Мне нравится просто воспроизводить звук и играть с высотой тона, чтобы понять, где он звучит хорошо. Откройте компонент AudioSource, который содержит звук шагов, и переключите ползунок Pitch! Обновляться будет в режиме реального времени.
Рис. 12.56. Компонент AudioSource
Вы услышите, что слишком высокий или слишком низкий звук создает довольно нереалистичный звук. Поэтому мне нравится придерживаться диапазона от 0.3 до –0.3. В нашем коде давайте добавим простой Random.Range(), ориентируясь на высоту звука компонента AudioSource.
290 Последние штрихи
Рис. 12.57. Компонент AudioSource, показывающий, как достигается случайная высота тона
Вот и все, что нам нужно! Один из наиболее важных способов создать глубину звукового ландшафта нашей игры – добавить как можно больше источников. И добавление таких вещей, как случайные вариации, звук для мелких деталей в анимации и динамическое аудио, может иметь большое значение! Поиграйте в игру, чтобы услышать проделанные изменения.
Заключение В заключительной главе было рассмотрено множество различных инструментов, с которыми мы работали в рамках нашего проекта. Потребовалось некоторое время, чтобы пройтись по процессу доработки арта и ассетов. Мы сосредоточились не только на моделях и текстурах, но и на проверке дизайна, чтобы убедиться, что каждый ассет вписывается в мир, как и ожидалось. В рамках этого мы также рассмотрели добавление эффектов из систем частиц Shuriken и VFX Graph, что включало реализацию эффектов для демонстрации телекинеза. Затем мы приступили к проектированию освещения. Разобрали освещение Unity с картами освещения, отражением, датчиками света и запеканием. Освещение в состоянии буквально перевернуть вашу игру, поэтому к этой части нельзя относиться легкомысленно! Далее, чтобы завершить игру, мы прошли доработку звука, чтобы запускать звуки с помощью анимации и добавлять рандомизированные звуки для оживления игрового процесса. Книга пройдена! Большое спасибо за то, что дочитали до конца, и мы надеемся, что это дало вам много знаний. Пожалуйста, рассмотрите возможность присоединиться к серверу Discord, где мы сможем ответить на ваши вопросы и более подробно рассказать о проекте. Следом вы найдете бонусную главу, где рассказывается о некоторых других инструментах Unity, которые можно использовать для различных проектов, а также о некоторых продуктах, которые Unity может предложить для многопользовательской игры, XR и визуальных скриптов. Дайте нам знать, если вам нужна книга на данные темы!
13 Бонус: другие инструменты Unity! В книге мы прошли через вертикальный срез игры-головоломки от третьего лица. Это специфический жанр игр со своими уникальными нюансами в разработке, и это означает, что многие замечательные инструменты Unity не были упомянуты, поэтому мы хотели выделить небольшой раздел и написать о том, что же еще интересного Unity может предоставить вам для следующего проекта. На уровне обобщения мы рассмотрим следующее: мультиплеер, XR, агент со средствами машинного обучения, Bolt.
Игровые сервисы Unity Мы изучили группу инструментов под названием Unity Gaming Services, или UGS. Эти инструменты предназначены для предоставления решений, разработка которых потребует значительного времени и которые могут быть быстро интегрированы в ваш проект. Эти инструменты могут помочь непосредственно с настройкой многопользовательской игры, XR (которая представляет собой смесь виртуальной и дополненной реальности), визуальных скриптов (известных как Bolt) и, наконец, группы инструментов для творческого рабочего процесса. Давайте сначала рассмотрим доступные инструменты для мультиплеера.
Инструменты для мультиплеера Unity предоставляет множество услуг, начиная от направляющих инструментов и обучения, заканчивая успешной реализацией этого многопользовательского фактора в вашем проекте. Разбивая категорию многопользовательской игры, Unity делит фокус на три столпа: Создание, Соединение и Взаимодействие.
Создание Unity считает это основой вашей игры и предлагает опции Netcode для GameObjects и Netcode для Entities. Netcode для GameObjects – это новая сетевая
292 Бонус: другие инструменты Unity! библиотека, созданная для игрового движка Unity, содержащая библиотеки, туториалы и примеры, которые можно настраивать и расширять в соответствии с потребностями вашего следующего многопользовательского проекта. Netcode для Entities использует преимущества нового технического стека, ориентированного на данные (DOTS – Data-Oriented Technology Stack) Unity. Этот новый высокопроизводительный многопоточный DOTS позволяет вам в полной мере использовать преимущества многоядерных процессоров для создания более крутого пользовательского интерфейса и более быстрого выполнения итераций с кодом C#, который легче читать и повторно использовать в других проектах. DOTS предоставляет программистам интуитивно понятную песочницу для создания безопасного многопоточного кода для множества преимуществ многопользовательской разработки. Ориентируясь на потоковые данные, DOTS обеспечивает гибкость повторного использования кода, помогает другим понять его и улучшает совместную работу. Имея эти различные возможности, DOTS преобразует ранее существовавшие рабочие процессы в рабочий процесс преобразования. Этот рабочий процесс преобразует ваши игровые объекты в объекты одним щелчком мыши. Во время выполнения Entity Preview Inspector визуализирует, как DOTS преобразует ваши игровые объекты в сущности. В тандеме использование функции Unity Live Link позволит вам мгновенно выполнять итерации в игровом режиме, не создавая каждый раз новую сборку. Использование более быстрой итерации без необходимости каждый раз создавать новую сборку позволяет вам и вашей команде тестировать игровой процесс на целевых устройствах в режиме реального времени. Unity создала пакеты DOTS для использования со своим стеком полезных ассетов. Они настоятельно рекомендуют вам использовать предварительные пакеты для тестирования и подготовки к производству ваших проектов в настоящее время, поскольку они проверяют пакеты, чтобы они были готовы к производству.
Соединение Пользовательский опыт в многопользовательском игровом процессе часто связан с подбором игроков, лобби до и после игры и временем ожидания. Unity разработала сервисы, дополняющие эти совместные действия через Lobby, Relay, Multiplay и Matchmaker. Unity Lobby предоставляет игрокам возможность подключаться до или во время игровой сессии. Публичные лобби могут быть созданы игроками с использованием простых игровых атрибутов. Эти лобби могут быть обнаружены другими игроками, и они смогут присоединиться. Приватные лобби позволяют игрокам создавать эксклюзивные пространства для приглашенных гостей. Relay – это сервисный инструмент, предлагаемый Unity, который хорошо сочетается с Lobby. Когда игрок отключается от игры, Relay автоматически выкидывает отключенных игроков и уведомляет вас о неожиданных отключениях. Кроме того, вы можете получить доступ к надежной основе для вашей совместной игры с помощью Netcode для GameObjects. Multiplay предлагает отказоустойчивый многооблачный гибридный игровой сервер с помощью Unity. Эта услуга позволяет вам получить доступ к хостингу
Игровые сервисы Unity 293 и сопоставлению серверов без необходимости создавать и поддерживать собственную серверную инфраструктуру. Стремясь обеспечить более плавный игровой процесс, Unity вложились в создание более 190 центров обработки данных, обеспечивающих отказоустойчивость и производительность в любом масштабе. Эти серверы работают по всему миру, поддерживая стандарт качества обслуживания (QoS – Quality of Service). QoS находит оптимальный регион для подключения к матчу, предоставляя вашим игрокам наиболее стабильное соединение, где бы они ни играли, тем самым максимизируя вовлеченность игроков, сокращая время ожидания и доставляя новый контент вашим игрокам независимо от платформы. Matchmaker с открытым исходным кодом под названием Open Match создан в результате сотрудничества с Google. Matchmaker также является частью решения Multiplay для хостинга выделенных игровых серверов Unity, предлагающего готовую интеграцию для корпоративных клиентов Unity, которая может масштабироваться для различных базовых возможностей игроков. Matchmaker содержит настраиваемую разработчиком логику матча, настраиваемый оценщик и цикл подбора матчей с запланированным выполнением функции матча в качестве решений для хостинга, позволяющих соединять ваших игроков в нужном месте и в нужное время.
Взаимодействие Вовлечение и удержание игроков происходит, когда опыт является здоровым, захватывающим и имеет стабильное качество их взаимодействия друг с другом. Unity предлагает инструменты взаимодействия с игроками для поддержки положительного социального опыта через Vivox и Community and Safety. Vivox – это простая в реализации и надежная функция, которая поддерживает многофункциональный сервис голосового и текстового чата для вашей игры. Vivox, которому доверяют ведущие в отрасли игры, такие как Valorant, League of Legends, Rainbow Six Siege и PUBG, предоставляет своим игрокам лучшие услуги связи. Работая с любой платформой, по всему миру и на любом игровом движке, Vivox можно интегрировать менее чем за два дня и расширить для миллионов игроков. Скоро появится новое дополнение к арсеналу взаимодействия Unity – «Комьюнити и безопасность». В центре внимания Unity с этой платформой – анализ безопасности игроков и управление игрой.
Плагин XR XR – это общий термин, который охватывает следующие приложения: виртуальную реальность (VR – Virtual Reality) и дополненную реальность (AR – Augmented Reality). VR создает уникальную среду вокруг своего пользователя. AR накладывает цифровой контент на реальный мир через призму технических устройств. XR применяет комбинацию как среды реального мира пользователя, так и цифрового мира для взаимодействия друг с другом. Unity разработала новую структуру плагинов под названием XR SDK. Эта структура позволяет поставщикам XR успешно интегрироваться с движком Unity и всеми его функциями для оптимизации каждого приложения, упомянутого ранее.
294 Бонус: другие инструменты Unity! Unity поддерживает следующие платформы для XR: ARKit, ARCore, Microsoft HoloLens, Windows Mixed Reality, Magic Leap, Oculus, OpenXR, PlayStation VR. В настоящее время Unity не поддерживает XR в WebGL. Подход на основе плагинов позволяет Unity адаптироваться за счет быстрого исправления багов, распространения обновлений SDK от партнеров по платформе и поддержки новых устройств XR без необходимости модификации процессора ядра.
Агент со средствами машинного обучения Unity понимает, что продвижение исследований в области искусственного интеллекта (ИИ) зависит от решения сложных проблем в существующих средах с использованием текущих эталонных показателей для обучения моделей ИИ. Когда игра становится сложной, разработчикам необходимо создавать интеллектуальное поведение, что может привести к написанию тонны кода с использованием узкоспециализированных инструментов. Unity создала набор инструментов для агентов со средствами машинного обучения (ML-Agents), который означает, что вам больше не нужно «кодировать» непредсказуемое поведение, а вместо этого учить умных агентов «учиться» с помощью комбинации глубокого обучения с подкреплением и имитации обучения. Таким образом, разработчикам можно создавать среды ИИ, более богатые физически, визуально и когнитивно, а также с более привлекательным игровым процессом и расширенными игровыми возможностями.
Визуальный скриптинг Bolt Визуально разрабатывайте игровые механики с логикой взаимодействия. Bolt позволяет создавать визуальные графические системы вместо написания традиционных строк кода. С помощью визуального скриптинга Bolt способствует беспрепятственному сотрудничеству между командами дизайнеров, художников и программистов для более быстрого прототипирования и итерации. Команды технических специалистов могут расширить возможности непрограммистов с помощью настраиваемых нод, чтобы повысить производительность независимо от уровня знаний в области программирования. Bolt предлагает Flow Graphs, State Graphs, Live Editing, Debugging and Analysis, Codebase Compatibility и Ease of Use, которые мы опишем ниже.
Flow Graphs Flow Graphs станут основным инструментом взаимодействия в ваших проектах. Используя действия и значения на основе нод, вы сможете диктовать логику в любом указанном вами порядке.
Заключение 295
State Graphs State Graphs позволяют создавать автономные модели поведения, которые определяют, какие действия следует выполнять, когда они достигают определенного состояния. State Graphs совместимы с высокоуровневой логикой, такой как поведение ИИ, структура сцены или уровня или любое поведение для перехода между состояниями.
Live Editing Live Editing можно выполнять в режиме реального времени в Play Mode. Быстро итерируйте и тестируйте идеи без необходимости повторной компиляции изменений проекта.
Debugging and Analysis Debugging and Analysis можно найти в игровом режиме на графике с визуальным скриптом. Bolt подсветит, какие ноды выполняются, а затем, если произойдет ошибка, нода будет легко идентифицирована.
Codebase Compatibility Codebase Compatibility позволяет использовать любые сторонние плагины или пользовательские скрипты в ваших графиках. Визуальные скрипты получают доступ к вашей всегда актуальной кодовой базе напрямую через отражение независимо от того, является ли это методом, полем, свойством или событием из Unity.
Ease of Use Ease of Use предназначены для менее технических авторов благодаря удобным функциям, комментированиям и возможностям поиска.
Заключение Цель бонусной главы состояла в том, чтобы показать некоторые возможности, которые Unity может предложить вам в ваших следующих начинаниях по разработке игр. В нашем проекте они не использовались, поэтому мы оставили их в бонусной главе. Инструменты мильтиплеера – отличный источник для создания многопользовательских игр. Объединив плагины AR и VR в плагин XR, мы получили хороший централизованный плагин для всех измененных реальностей, независимо от используемого вами оборудования. В заключение этой книги мы поговорили о визуальном скриптировании Bolt. Если вам сложно работать с программированием, подумайте об использовании Bolt. Надеемся, что вы узнали что-то новое из этой книги и хорошо провели время. Мы упоминали в начале и на протяжении всей книги, что сообщество Discord – это хорошее место для общения с другими людьми, купившими эту или другие книги, изданные Packt и ориентированные на Unity. Мы будем рады видеть вас там и помочь с вашей следующей отличной идеей.
Предметный указатель Б
Бенчмаркинг 236
В
Вектор 25 Вершины 26 Выдавливание 115
Г
Грань 26
И
Игровой цикл 119 Инверсная кинематика 72 Инверсная кинематика человека 75
К
Камеры 25 Конвейер рендеринга высокой четкости 46 Контроллер 76
М
Материал 26 Меш 26
Н
С
Создатель цифрового контента 23 Сопрограмма 155 Статические объекты 146
Т
Тернарный оператор 87
У
Универсальный конвейер рендеринга 46 Циклы while 60
A
Action Maps 82
B
Base Map 27
C
Cascade 278
E
Extrapolate 144
H
HLSL 188
Наслоение 218 Нода 200
I
О
L
Interpolate 143
Обнаружение столкновений 145 Огибающая 214 Окружающие звуки 227 Оператор if 60 Отладчик физики 243
Lerp 135 Lightmap 276
П
S
Панорамирование 222 Плейн 26 Пользовательский интерфейс 159 Прямая кинематика 72
Р
Ребро 26
P
PlayerInput 81 Profiler 236 Shader Graph 188 ShaderGraph 47 Shuriken 188
V
VFX Graph 188 VFXGraph 47
Книги издательства «ДМК Пресс» можно заказать в торгово-издательском холдинге «КТК Галактика» наложенным платежом, выслав открытку или письмо по почтовому адресу: 115487, г. Москва, пр. Андропова д. 38 оф. 10. При оформлении заказа следует указать адрес (полностью), по которому должны быть высланы книги; фамилию, имя и отчество получателя. Желательно также указать свой телефон и электронный адрес. Эти книги вы можете заказать и в интернет-магазине: www.galaktika-dmk.com. Оптовые закупки: тел. (499) 782-38-89. Электронный адрес: [email protected].
Энтони Дэвис, Трэвис Батист, Рассел Крейг, Райан Станкел
Разработка 3D-игр в Unity
Главный редактор
Мовчан Д. А.
[email protected]
Зам. главного редактора Перевод Корректор Верстка Дизайн обложки
Сенченкова Е. А. Бомбакова П. М. Абросимова Л. А. Луценко С. В. Мовчан А. Г.
Формат 70×100 1/16. Гарнитура «PT Serif». Печать цифровая. Усл. печ. л. 24,21. Тираж 200 экз. Веб-сайт издательства: www.dmkpress.com