Базовые концепции Unity для программистов

Блог компании Издательский дом «Питер»ПрограммированиеРазработка игрC#Unity
Автор оригинала: Eyas Sharaiha
Привет, Хабр! При проработке темы Unity мы нашли интересный блог, возможно, заслуживающий вашего более пристального внимания. Предлагаем вам перевод статьи о базовых концепциях Unity, также опубликованный на портале Medium


Если вы обладаете опытом программирования и пытаетесь вкатиться в разработку игр, порой бывает непросто найти такие учебные материалы, в которых в достаточной степени объясняется необходимый контекст. Вероятно, придется выбирать между материалами, в одних из которых описана парадигма ООП, в других — язык C# и концепции Unity, либо сразу начинать с продвинутых руководств; в последнем случае придется самостоятельно дедуктивно выводить базовые концепции.

Поэтому, чтобы отчасти заполнить этот пробел, я решил написать серию статей Unity for Software Engineers. Это первая из них. Статья рассчитана на читателей, имеющих представление о программировании и программной архитектуре, в особенности на тех, кому близок тот же подход к обучению, что и мне: начинать с основ и постепенно идти вверх.

Я начал путь в программировании около 17 лет назад, открыв для себя Game Maker. Многие часы потратил на самостоятельное программирование маленьких игр и инструментов, в процессе всерьез увлекшись программированием.

Однако с тех пор ландшафт игровой разработки серьезно изменился. Когда я попробовал Unity после долгой паузы с игровой разработкой, мне больше всего хотелось понять базовые концепции: из каких кирпичиков строится игра в Unity? Что нужно знать о принципах представления этих кирпичиков в памяти или на диске? Как организован идиоматический код? Какие паттерны предпочтительны?

Сцена


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

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



Редактор сцен Unity, в котором загружена задаваемая по умолчанию пустая сцена в режиме 3D. В пустых сценах Unity3D по умолчанию содержатся объекты Main Camera (Главная Камера) и Directional light (Направленный свет).



Пример сцены в редакторе Unity; здесь выделено несколько объектов. Такое представление сцены можно использовать для редактирования уровней в игре.

Каждый игровой объект в Unity должен находиться в сцене.

Игровые объекты


Игровой Объект (в коде GameObject) – один из базовых кирпичиков, из которых строится игра.

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

У каждого игрового объекта есть значения положения и поворота. Для метафизических объектов они не имеют значения.

Допускается вложение игровых объектов друг в друга. Положение и поворот каждого объекта отсчитывается относительно его родительского объекта. Объект, расположенный непосредственно в сцене, позиционируется относительно «мировых координат».



Группа объектов, совместно вложенных в сцене и объединенных в пустом объекте “Interior_Props”, сделано в целях структурирования

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



Группа объектов, вложенных в объект «игрок». Здесь мы видим оружие игрока, его аватарку и различные элементы пользовательского интерфейса, отображаемые вокруг игрока

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

Компоненты (и моноповедения)




Объект «Warrior» с предыдущего скриншота показан над окном «Инспектор» в интерфейсе Unity. Каждый из проиллюстрированных разделов (напр., Animator, Rigidbody, Collider) – это компоненты, слагающие этот объект

Каждый игровой объект состоит из компонентов.

Компонент реализует четко определенный набор поведений, необходимых, чтобы мог выполниться GameObject. Все, благодаря чему объект получается таким, каков он есть — это вклад компонентов, из которых он состоит:

  • У единственного «видимого» элемента машины будет компонент Renderer, который отрисовывает машину и, вероятно, компонент Collider, задающий для нее границы столкновений.
  • Если машина представляет персонажа, то у самого объекта car может быть Player Input Controller (Контроллер ввода от персонажа), принимающий все события, связанные с нажатиями клавиш, и транслирующий их в код, отвечающий за движение машины.

Притом, что можно писать большие и сложные компоненты, где компонент 1 в 1 равен кодируемому объекту (напр., компонент player содержит код, полностью описывающий персонажа, а компонент enemy, в свою очередь, полностью кодирует противника) обычно принято извлекать логику, дробя ее на небольшие «обтекаемые» кусочки, соответствующие конкретным признакам. Например:

В коде есть MonoBehavior, вездесущий родительский класс для представления компонентов. Большинство невстроенных компонентов будут наследовать от MonoBehavior, который, в свою очередь, наследует от Behavior и Component, соответственно.

  • Все объекты, обладающие здоровьем, будь то Player (Игрок) или Enemy (Враг) могут иметь компонент LivingObject, задающий исходное значение здоровья, принимающий урон и приводящий в исполнение смерть, когда объект умирает.
  • Кроме того, у игрока может быть компонент ввода, контролирующий сообщаемые ему движения, а у врага может быть аналогичный компонент, реализованный при помощи искусственного интеллекта.

На протяжении жизненного цикла компоненты получают различные обратные вызовы, которые в среде Unity именуются Сообщениями. К Сообщениям относятся, в частности, OnEnable/OnDisable, Start, OnDestroy, Update и другие. Если объект реализует метод Update(), то этот метод будет как по волшебству вызываться Unity в каждом кадре игрового цикла, пока объект активен, а заданный компонент действует. Эти методы могут быть помечены private; в таком случае движок Unity все равно будет их вызывать.

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

Ресурсы


Ресурсы – это расположенные на диске сущности, из которых состоит игровой проект. К ним относятся сети (модели), текстуры, спрайты, звуки и другие ресурсы.

Когда ваши сцены сериализуются на диск, система представляет их в виде ресурсов, состоящих из игровых объектов внутри них. В следующем разделе мы также рассмотрим, как превратить часто переиспользуемые игровые объекты в ресурс под названием «шаблонные экземпляры» (prefab).



Также ресурсы могут представлять менее «осязаемые» объекты, например, карты контроля ввода, графические настройки, строковые базы данных для интернационализации и многое другое. Также можно создавать собственные типы ресурсов при помощи ScriptableObjects. Вот статья о том, как сохранять такие вещи.

Для проекта, находящегося в разработке, ресурсы – это ключевая информационная составляющая базы кода, наряду с кодом как таковым.

В готовом пакете с игрой будет содержаться большинство ваших ресурсов. Они будут сохранены на диске на том устройстве, где установлена игра.

Шаблонные экземпляры


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

Вложенные шаблоны


Начиная с Unity 2018.3, поддерживается вложение шаблонов, чего и следовало ожидать:

  1. Родительский объект с дочерними объектами, представленными в виде шаблонов, сам может быть представлен в виде шаблона. Внутри родительского шаблона дочерний шаблон допускает собственные модификации. В сцене инстанцируется сразу вся иерархия шаблонов, а поверх нее также могут надстраиваться модификации, специфичные для конкретной сцены.
  2. Шаблонный экземпляр, находящийся в сцене и снабженный собственными локальными модификациями, может быть сохранен как самостоятельный ресурс «Prefab Variant». Этот вариант представляет собой шаблонный ресурс, наследующий от другого шаблона, поверх которого применены дополнительные модификации.

Эти концепции компонуются: возможен шаблонный вариант вложенного шаблона или, например, шаблонный вариант шаблонного варианта.

Сериализация и десериализация


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

Поток сериализации/десериализации, действующий в движке Unity, загружает в память ресурсы, расположенные на диске (в вашем проекте: для редактирования или тестового прогона игры, либо в самой игре, при загрузке сцены) и отвечает за сохранение состояния отредактированных вами объектов и компонентов обратно в соответствующие сцены и шаблонные экземпляры.
Следовательно, система сериализации также является ключевым элементом работы с редактором Unity. Чтобы MonoBehavior мог принять ввод при конструировании сцены в ходе ее инициализации, эти поля должны быть сериализованы.

Большинство базовых типов Unity, в частности, GameObject, MonoBehavior и ресурсы поддаются сериализации и могут получать исходные значения при создании прямо из редактора Unity. Публичные поля в вашем MonoBehavior сериализуются по умолчанию (если относятся к сериализуемому типу), а приватные поля для этого сначала нужно пометить атрибутом Unity [SerializeField], и тогда они тоже могут быть сериализованы.


Скриншот игры Chaos Reborn производства Snapshot Games, 2015 год. BY-CC-SA 3.0

Заключение


Выше мы рассмотрели основные структурные концепции, используемые в архитектуре игр Unity. Почитав подробнее о них, а также о том, как хранимые на диски ресурсы соотносятся с представлениями этих ресурсов в памяти, вы должны получить представление о движке, после чего можно переходить к изучению более продвинутых руководств.
Теги:UnityC#разработка игригровой движок
Хабы: Блог компании Издательский дом «Питер» Программирование Разработка игр C# Unity
+11
5,7k 77
Комментарии 6

Похожие публикации

Лучшие публикации за сутки

Информация

Дата основания
Местоположение
Россия
Сайт
piter.com
Численность
201–500 человек
Дата регистрации

Блог на Хабре