Организация проекта в Unity.

Организация директорий, создание и использования основных скриптов, создание и настройка сцен.

В этой статье мы поговорим о:

Технологиях рендера Unity;

Организации папки Assets;

Основных структуро-основополагающих скриптах;

И о организации сцен в движке Unity.

Сегодня речь пойдет о моем, на данный момент, основном инструменте, о Unity (а именно о версии 6000.2.8f1, на момент написания. последняя в official releases). Сейчас, я хотел бы начать с самого начала, а именно с момента создания проекта в Unity Hub, ведь с версией Unity 2018.1, появилось разделение не только на 2d и 3d проекты, была добавлена возможность выбрать рендер-пайплайн (Render Pipelines), включая Universal Render Pipeline (URP), High Definition Render Pipeline (HDRP) и встроенный (Built-in) пайплайн.

Стоит вкратце рассказать в чем же отличие между всеми этими вариантами рендера.

URP позволяет вам разрабатывать свои проекты на более чем 17 поддерживаемых им платформах.

URP (Universal Render Pipeline) изначально предоставляет три пути рендеринга для лучшей поддержки разнообразных игр, которые вы можете разрабатывать с помощью Unity. Прямой рендеринг, обеспечивает оптимизированные рабочие процессы с материалами и освещением, которые хорошо масштабируются для всех поддерживаемых платформ. Отложенный рендеринг обеспечивает возможность рендера большого количества источников света без существенного снижения производительности, обычно связанного с методами прямого рендеринга. Это отличный вариант для более сложных визуальных эффектов, адаптированных для мобильных устройств, консолей и настольных компьютеров более высокого класса. А 2D-рендеринг обеспечивает свет и тени в реальном времени для 2d игр.

High Definition Render Pipeline (HDRP) использует физически обоснованные методы освещения, линейное освещение, HDR-освещение и настраиваемую гибридную архитектуру отложенного/прямого освещения Tile/Cluster. Он предоставляет вам инструменты, необходимые для создания приложений, таких как игры, технические демонстрации и анимации, на высоком графическом уровне.

Проекты созданы с помощью HDRP не совместимы с URP.

Встроенный пайплайн (Built-in Render Pipeline) - это стандартный конвейер рендеринга, который использовался в старых версиях Unity по умолчанию, но он имеет ограниченные возможности настройке и не является лучшим выбором для новых проектов.

Поэтому я, для небольших и не детализированных с точки зрения освещения проектов, не требующих наличие ray tracing и других подобных технологий, выбирать тип проекта Universal 3d/2d, ну и стоит сказать что HDRP, на данный момент предназначен только для 3d проектов и не поддерживает двухмерные игры, для которых в любом случае нужно будет использовать URP Pipeline или Built-in Render Pipeline.

Теперь раз уж мы определились в выборе технологии рендера, стоит поговорить о минусах URP, в основном отмечают такие ошибки как “Падение производительности на старых устройствах”, и например “ошибки шейдеров”, что в большинстве своем было исправлено к последним версиям движка.

"Только что созданных проект Unity.6000.2.8f1."
"Только что созданных проект Unity.6000.2.8f1."

Первым делом после создания проекта стоит занятся установкой или обновлением всех нужных модулей или asset’ов, в Package Manager, например (TMP - text mesh pro), Unity Particle Pack, или например Mirror. (В скором времени я планирую опубликовать подробную статью про некоторые asset’ы для Unity).

"Package Manager Unity.6000.2.8f1."
"Package Manager Unity.6000.2.8f1."

Дальше, я обычно советую, немного прибраться в установленных asset’ах, а именно удалить различные ненужные папки и файлы. Например я советую удалять стандартную папку в корневой директории assets - TutorialInfo, ведь она не содержит никакой полезной информации и просто загромождает проект, и по таким же суждениям советую удалить файл Readme, там всего лишь указана ссылка на информационные материалы по URP, ну и стандартная папка Scenes нам пока что не нужна. Что же касается папок установленных asset'ов я не советую что либо в них удалять (это может помешать их работе), максимум вы можете удалить лишние шейдеры и не нужные тестовые сцены.

В итоге у нас остается папка Settings, файл новой Input System - InputSystem_Actions, и соответственно папки установленных вами asset'ов, например папка TextMesh Pro (для TMP), или UnityTechnologies (для Particle Pack).

"Чистая папка Asstes в проекте."
"Чистая папка Asstes в проекте."
"Папка Assets со 'стандартными', дополнительными пакетами."
"Папка Assets со 'стандартными', дополнительными пакетами."

Теперь можно перейти к этапу самостоятельного создания различных папок, например очень часто выделяют такую модель организации, где все ресурсы, непосредственно самой игры, находятся в отдельной папке, чаще всего, с названием самой игры, также довольно часто в начале названия этой папки пишут “!”, чтобы она всегда была первой в списке. А остальные папки в корневой директории - это папки различных asset’ов и пакетов. И ниже я будут описывать именно эту модель. Также можно создавать например папку Plugins, и в нее поместить сторонние asset’ы, но есть такая проблема что не все asset’ы могут корректно работать находясь не в корневой папке проекта, но например вышеупомянутые TextMesh Pro и UnityTechnologies Particle Pack, могут спокойно работать не находясь в ней, а например такому плагину как Mirror - для создания онлайн-проектов, необходимо находиться в корневой директории решения. Исходя из этого создания папки Plugins остается на совести разработчика.

Теперь перейдем к принципам организации основной директории с ресурсами проекта. “!ProjectName”. Здесь опять выделяют в основном два принципа организации директорий.

Первый вариант.

Папка с ресурсами, часто именуемая просто “Resources”, содержит в себе директории с названием описывающем какой либо игровой объект, и содержащая в себе основные его ресурсы а именно его 3d модель, материал, текстуру, прилагающиеся локальные скрипты, и unity.prefab, что как бы делает эту папку контейнером для определенного игрового объекта. Глобальные скрипты такие как абстракции (абстрактные скрипты) или скрипты основывающие архитектуру проекта находятся в другой папке - “Scripts”.

"Контейнер для объекта Сундук."
"Контейнер для объекта Сундук."

В данном исполнении директория “Scripts” условно разделяется на две области. В первой области находятся скрипты “первой необходимости”, например скрипты, отвечающие за архитектуру проекта, например: ResourcesManager, EventBusSystem, ServiceLocator, EntryPoint. А во второй области находятся менее значимые скрипты, например: абстрактные классы, или локальные менеджеры.

"Пример разделения папки Scripts."
"Пример разделения папки Scripts."

Второй вариант.

Второй принцип предлагает организовать проект следующим образом, директория “!ProjectName”, содержит в себе папки “Resources”, “Scenes”, “Scripts”, и возможно папку StreamingAssets. (папки scenes и StreamingAssets, безусловно также используются в первом варианте организации проекта, но просто в повествовании они описаны ниже).

*Многие asset'ы в Unity комбинируются при сборке в проект. Тем не менее, иногда полезно размещать файлы на указанном компьютере в нормальной файловой системе, чтобы сделать их доступными через пути. Все файлы, помещенные в папку под названием StreamingAssets в Unity проекте будут скопированы в определённую папку на указанный компьютер. Вы можете извлечь папку используя свойство Application.streamingAssetsPath.

Если же с папкой Scenes вроде бы все легко, и ее стоит разделять только на тестовые и непосредственно геймплейные сцены (и скорее всего их уже в свою очередь разбивать на другие папки, в зависимости от специфики вашего проекта, например: на папки World1 -> Location1). То например с директориями Resources или Scripts все немного сложнее.

С начала бы я хотел рассказать об основной папке со всем “ресурсами” проекта (в данном контексте “ресурсы” - это различные 3d модели, текстуры, материалы, префабы и тд.), эта директория разделяется на более мелкие по типам ресурсов хранящихся в них.

И в итоге получается как то так:

Организация проекта в Unity.

Такая организация данной папки очень удобна для использования модели (паттерна), где всеми ресурсами проекта “руководит” единый менеджер ресурсов.

Что же на счет папки "Scripts", эту папку обычно разделяют на три основные области, в первой находятся все “самые главные” скрипты, отвечающие за архитектуру проекта, например: ResourcesManager, EventBusSystem, ServiceLocator, EntryPoint. Во вторую область определяют скрипты чуть менее значимые, например абстрактные классы, или локальные менеджеры. А третью область из-за ее специфики разделяют на мелкие папки, в которых описаны мелкие, локальные реализации, локальных фитч, или конкретные реализации абстрактных классов из второй области.

Основные скрипты.

Как я уже писал выше к этой категории я отнес подобные скрипты: EventBusSystem, ResourcesManager, ServiceLocator, EntryPoint. В этой статье я бы хотел разобрать только ResourcesManager (менеджер ресурсов), и EventBusSystem (шина событий).

EventBus (шина событий) – это архитектурный паттерн проектирования, обеспечивающий взаимодействие между слабо связанными компонентами системы по принципу "издатель-подписчик" (publish-subscribe).

Принцип работы.

EventBus действует как центральный маршрутизатор или посредник, через который компоненты могут обмениваться сообщениями (событиями), не зная о существовании друг друга.

Сам паттерн довольно старый и имеет свое начало в книге “Design Patterns: Elements of Reusable Object-Oriented Software”, выпущенной аж в 1994 году. И этот паттерн долгое время применялся и применяется по сей день, в языке Java, и со временем почти без изменений попал в C#, затем с развитием игровой индустрии и усложнением проектов этот паттерн пришел в геймдев, в частности в Unity, и другие подобные ему игровые движки.

Основная суть данного паттерна как говорилось в определение выше - это создание связей по типу “издатель-подписчик” между не знающими друг о друге компонентами. Рассмотрим этот скрипт на примере.

скрипт имеет основные две функции:

public void Subscribe(Action subscriber)

{

if (!signals.ContainsKey(typeof(T)))

{

signals.Add(typeof(T), new List() { });

}

signals[typeof(T)].Add(subscriber);

}

и,

public void Invoke(T signal)

{

if (signals.ContainsKey(typeof(T)))

{

foreach (object subscriber in signals[typeof(T)])

{

Action action = subscriber as Action;

action?.Invoke(signal);

} } }

Основной принцип работы.

Централизованное хранилище - система хранит все подписки на события в словаре.

Подписка/отписка - объекты регистрируют свои методы для получения событий.

Рассылка - при возникновении события все подписчики получают уведомление.

Пример использования.

// Пример класса события

public class PlayerDamageEvent

{

public int DamageAmount;

public Vector3 DamagePosition;

public GameObject Attacker;

public PlayerDamageEvent(int damage, Vector3 position, GameObject attacker)

{

DamageAmount = damage;

DamagePosition = position;

Attacker = attacker;

}

public class PlayerHealth : MonoBehaviour

{

private void OnEnable()

{

// Подписываемся на событие получения урона

GameManager.EventBus.Subscribe(OnPlayerDamaged);

}

private void OnDisable()

{

GameManager.EventBus.UnSubscribe(OnPlayerDamaged);

}

private void OnPlayerDamaged(PlayerDamageEvent damageEvent)

{

// Обрабатываем получение урона

TakeDamage(damageEvent.DamageAmount);

ShowDamageEffect(damageEvent.DamagePosition);

}

public class EnemyAttack : MonoBehaviour

{

public int AttackDamage = 10;

private void AttackPlayer()

{

// Создаем событие

var damageEvent = new PlayerDamageEvent(

AttackDamage,

transform.position,

gameObject

);

// Вызываем событие

GameManager.EventBus.Invoke(damageEvent);

} }

ResourcesManager.cs.

Этот скрипт представляет собой менеджер ресурсов (Resources Manager), который централизованно загружает, хранит и предоставляет доступ к ресурсам игры. Он состоит из двух основных методов. Разберем на примере загрузки аудио.

private void Awake()

{

if (Instance == null)

{

Instance = this;

DontDestroyOnLoad(gameObject);

}

else

{

Destroy(gameObject);

}

// Загружаем все ресурсы из соответствующих папок Resources

music = Resources.LoadAll("Audio/Music");

sounds = Resources.LoadAll("Audio/Sounds");

}

public AudioClip FindMusic(string name)

{

foreach (AudioClip clip in music)

{

if (clip.name == name)

{

return clip;

}

}

return null;

}

Использование в коде.

AudioClip backgroundMusic = ResourcesManager.Instance.FindMusic("main_theme");

AudioClip jumpSound = ResourcesManager.Instance.FindSound("jump");

AudioSource audioSource = GetComponent();

audioSource.clip = ResourcesManager.Instance.FindMusic("level_music");

audioSource.Play();

Организация сцен в движке Unity.

Про организацию сцен, не то чтобы можно много говорить. Хоть сцены это основа Unity, каждый разработчик сам определяет то как ему это делать, нет каких-либо конкретных практик, структур и подобного. Скорее есть просто общепринятые нормы и рекомендации.

Одна из них достаточно банальна. Это просто не загромождать иерархию сцены различными объектами, и если объектов одного типа слишком много то следует их объединять дочерними для пустого объекта (обычно много это от 5 экземпляров одного объекта).

Второй совет. Для каждого значимого глобального скрипта (который влияет на всю сцену целиком) создавать отдельный объект на сцене, которые в свою очередь объединяются под объектом с названием по типу “Managers”.

Третий совет. Объекты у которых помимо transform, имеется дополнительно только один компонент в название добавлять “@” и название компонента, например “@music_AudioSource”.

"Пример объекта с только одним дополнительным компонентом."
"Пример объекта с только одним дополнительным компонентом."

Также, многие на сцене используют “разделители” - пустые объекты которые созданы, чтобы просто визуально, своим названием, разделить объекты в иерархии на различные по назначению блоки.

"Один из возможных примеров организации простой сцены."
"Один из возможных примеров организации простой сцены."

Итог.

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

Что же по поводу EventBus и ResourcesManger, EventBus советую использовать везде, кроме уж совсем маленьких и начальных проектов, а ResourcesManger лучше использовать в проектах с большим количеством разнообразных объектов.

Спасибо, что заглянули!

Полезные ссылки (links:) {

Ссылки на материалы:

Репозиторий:

Steam Profile:

Itch.io:

GitHub:

}

1
1 комментарий