КОНТРОЛЬ НАД ПРОЕКТОМ НА UNITY: ЧАСТЫЕ ПРОБЛЕМЫ НАЧИНАЮЩИХ РАЗРАБОТЧИКОВ И ПУТИ ИХ РЕШЕНИЯ
КОНТРОЛЬ НАД ПРОЕКТОМ НА UNITY: ЧАСТЫЕ ПРОБЛЕМЫ НАЧИНАЮЩИХ РАЗРАБОТЧИКОВ И ПУТИ ИХ РЕШЕНИЯ
Научная статья
Карелова Р.А.1, *, Коробейников П.С.2
1, 2 Нижнетагильский технологический институт (филиал) Уральского федерального университета имени первого Президента России Б.Н. Ельцина, Нижний Тагил, Россия
* Корреспондирующий автор (riya2003[at]mail.ru)
АннотацияШирокий спектр инструментов кроссплатформенной разработки компьютерных игр делают среду разработки Unity одной из самых популярных на сегодняшний день, в том числе для создания обучающих симуляторов, интерактивных дидактических материалов, систем человеко-машинной коммуникации и т.п. В статье выделены наиболее часто встречающиеся проблемы начинающих разработчиков, связанные с контролем над проектом, в частности, с управлением вызовами методов, заложенных на уровне библиотек Unity, и вызовами ресурсозатратных методов. Предложены пути решения обозначенных проблем.
Ключевые слова: unity, проблемы архитектуры, оптимизация, Singleton, разработка игры, скрипты, управление методами.
UNITY PROJECT CONTROL: FREQUENT PROBLEMS OF BEGINNING DEVELOPERS AND WAYS OF THEIR SOLUTION
Research article
Karelova R.A.1, *, Korobejnikov P.S.2
1, 2 Nizhny Tagil Technological Institute (branch) of the Ural Federal University named after the first President of Russia B.N. Yeltsin, Nizhny Tagil, Russia
* Corresponding author (riya2003[at]mail.ru)
AbstractA wide range of cross-platform development tools for computer games makes the Unity development environment one of the most popular today, especially for creating training simulators, interactive didactic materials, human-machine communication systems, etc. The paper highlights the most common problems of novice developers related to project control, in particular, with managing method calls implemented at the Unity library level and resource-intensive method calls. The authors propose ways of solving the problems mentioned above.
Keywords: unity, software architecture issues, optimization, Singleton, game development, scripts.
ВведениеВсе, кто, так или иначе, сталкивался с разработкой компьютерных игр, знакомы с программным обеспечением, позволяющим облегчить процесс разработки игрового проекта, предоставляя разработчику готовые программные компоненты. Одной из самых популярных для малобюджетных и среднебюджетных компаний по созданию и продвижению игр является среда разработки Unity. Большое количество инструментов для кроссплатформенной разработки, подробная документация, активная поддержка в виде регулярных обновлений позволяют достаточно быстро начать разрабатывать проекты даже тем, кто не является профессиональным программистом. Многие будущие разработчики начинают постигать основы программирования создавая свои первые игры.
Следует отметить, что сегодня среда разработки Unity применяется не только для разработки игр, но и для создания обучающих симуляторов [1], [4], [5], интерактивных дидактических материалов [3], систем человеко-машинной коммуникации для лиц с ограниченными возможностями [2], а также для решения задач реконструкции фасадов зданий [6].
Как и любая среда разработки, Unity имеет свои нюансы при создании и поддержке архитектуры проекта. Многие проблемы, часто встречающиеся в проектах, являются следствием ошибочного восприятия обучающих видео, которые создают для разработчиков Unity. Как правило, в них демонстрируется то, как пользоваться той или иной технологией и игнорируется правильная интеграция её в код проекта. Обучающие видео от сторонних разработчиков чаще всего направлены на лёгкость и скорость, что влечёт за собой дальнейшее построение архитектуры с большим количеством зависимостей и трудностями ее расширения в дальнейшем. Зависимости и отсутствие контроля вызывают усложнённую поддержку проектов, а также увеличивают сроки подключения к ним новых разработчиков. Самой частой проблемой разработчиков Unity является отсутствие контроля над порядком выполнения методов событий, а также неправильный или слишком частый вызов методов, которые негативно сказываются на производительности приложения.
Содержание многочисленных обсуждений на специализированных форумах, а также личный опыт авторов позволили выделить несколько основных проблем, с которыми сталкиваются начинающие программисты Unity.
Первая проблема в указанном контексте, на которую следует обратить внимание, заключается в управлении вызовами методов, которые заложены на уровне библиотек Unity. MonoBehaviour является базовым классом, унаследовав который, дочерний класс получает возможность реализации внутренних событий Unity. Класс, который наследуется от MonoBehaviour, может реализовывать такие методы как Update, вызывающий каждый кадр, Start, вызывающийся при инициализации класса, и т.п. (см. Листинг 1).
Листинг 1. Реализация методов событий using UnityEngine; public class Test : MonoBehaviour { private void Start() { // Выполняется при инициализации скриптов } private void Update() { // Выполняется каждый кадр } }Данные методы не имеют наследования или переопределения, они заложены на стороне библиотек, которые написаны на С++ и вызываются с помощью рефлексии. Вызов этих функций происходит в неизвестной разработчику иерархии. В Unity нет удобного средства управления последовательностью вызова этих событий. Многие начинающие разработчики пренебрегают решением этой проблемы и используют встроенные методы во всех своих скриптах. Это влечет за собой отсутствие контроля над кодом, что негативно скажется на управлении проектом при его увеличении, а также может вызвать непредвиденные ошибки [10].
Решением данной проблемы является централизация данных методов в одном скрипте, реализовав который появится возможность вручную обновлять необходимые классы и обновлять их состояние. Существует несколько подходов к централизации методов.
Один из подходов заключается в разработке скрипта, сообщающего классам о вызове методов DontDestroyOnLoad (см. Листинг 2). Этот метод создает сцену (если таковой еще не существует), которая будет существовать на протяжении всей жизни приложения, пока на ней есть какие-либо объекты, и помещает на эту сцену объект, передаваемый в качестве параметра. После этого в созданном классе нужно создать массив из тех, кто заинтересован в прослушивании событий, и при срабатывании того или иного события, вызывать свой метод, который будет являться аналогом события Unity.
Листинг 2. Вызов метода DontDestroyOnLoad using UnityEngine; public class Ticker : MonoBehaviour { private List<IEnvetsListner> _eventListners = new List<IEnvetsListner>(); private void OnEnable() { DontDestroyOnLoad(this); } private void Update() { foreach(listner in _eventListners) { listener.Tick(); // Tick является собственным методом, аналог Update } } private void AddEventListner(IEnvetsListner listner) { _eventListners.Add(listner); } }Плюсом данного подхода является упорядоченность и прямой вызов методов непосредственно из класса, сообщающего о событиях, и отсутствие возможности подписаться на эти события извне. Минусом такого подхода является необходимость связывать выделенную сцену и игровую при их инициализации с помощью GameObject.Find. Данный метод выполняет поиск объектов на сцене, осуществляя проход по каждому из них и сравнивая их имена со строкой, передаваемой в качестве параметра (см. Листинг 3).
Листинг 3. Реализация поиска объекта на сцене using UnityEngine; public class Test : MonoBehaviour, IEnvetsListner { private Ticker _ticker; private void Start() { var tickerObj = GameObject.Find(’’Ticker’’); _ticker = tickerObj.GetComponent<Ticker>(); _ticker.AddEventListner(this); } }Время выполнения данного метода пропорционально количеству объектов на сцене. Данный подход может быть применен, если сцены проекта не перегружены большим количеством объектов.
Другим вариантом решения проблемы является инициализация класса с помощью DontDestroyOnLoad и вызов метода с помощью статического класса или паттерна Singleton, на который впоследствии подпишется головной скрипт сцены и будет самостоятельно сообщать другим классам на сцене, что произошло то или иное событие. Плюсом данного подхода является отсутствие прямой зависимости от объектов сцены и их количества. Это же является и его минусом (особенно если над проектом трудится большое количество разработчиков), так как любой скрипт в проекте может подписаться на данные события. Данную проблему можно решить с помощью интерфейса для класса, который будет осуществлять оповещение объектов на текущей сцене о событии Unity. Это позволит ограничить объекты, которые подписаны на данное событие, и отследить классы, которые ошибочно реализуют данный интерфейс.
Второй часто встречаемой проблемой при построении архитектуры игровой сцены является излишний вызов ресурсозатратных методов. Такими, например, являются методы, вызываемые для создания и уничтожения объектов на сцене в течение работы приложения. Метод Instantiate, который создаёт объект на сцене, так же как и метод Destroy, который удаляет объект со сцены, влекут за собой создание и удаление объектов на уровне кода C++, и такие манипуляции негативно сказываются на времени, затрачиваемом на их выполнение. Даже вызов 10 пустых объектов с небольшой вложенной иерархией занимает много вычислительных ресурсов, что особенно негативно сказывается на производительности, если проект будет запущен на смартфонах (см. Рисунок 1).
Рис.1 − Затраты на создание пустых объектов
Если на этапе инициализации приложения или сцены этим временем часто можно пренебречь, то во время игрового процесса данную проблему нужно решать заранее. Самый частый способ решения данной проблемы - это создание пула объектов (GameObjectPool) (см. Листинг 4) [8], [9]. Вместо удаления со сцены объект помещается в массив ожидания для повторного использования. Если объект необходимо показать снова, он извлекается из массива и снова включается.
Листинг 4. Реализация GameObjectPool using UnityEngine; public class GameObjectPool<T> : MonoBehaviour where T : MonoBehaviour { private Stack< T > _pool = new Stack< T >(); private void GetObjectFromPool() { var obj = _pool.Pop(); If (obj == null) { obj = Instantiate(obj); } obj.SetActive(true); return obj; } private void ReleaseObject(T obj) { obj.SetActive(false); _pool.Push(obj); } }Данный способ имеет один недостаток. При первом обращении к объекту в любом случае вызывается метод Instantiate, так как данного объекта еще не существует. Во избежание данной проблемы объекты помещаются в GameObjectPool заранее, на этапе инициализации сцены. При реализации данного подхода чаще всего используют статичный класс с методами добавления и изъятия объектов из массива, который заново инициализируют при каждой смене сцены. Самой частой ошибкой при реализации данного подхода является создание единого контейнера для объектов, что вызывает необходимость смены родительского объекта в иерархии сцены. Данная операция зависит от количества дочерних элементов внутри объекта, у которого меняется родительский объект. И чем выше количество вложенных объектов, тем выше время данной операции. Особенно сильно это наносит ущерб производительности, если такая операция происходит внутри Canvas. Canvas - это объект для интерфейса в Unity, в котором отрисовка зависит не от координат, а от положения объекта в иерархии. Смена иерархии влечёт за собой полную перерисовку всего интерфейса, что пагубно отражается на производительности игры (см. рисунок 2).
Рис. 2 − Затраты на смену родительского объекта
В данном случае лучше не менять родительский контейнер, а отключать объект непосредственно в используемом месте, но также стоит помнить, что вместе с тем уйдёт и централизация отключенных объектов в одном месте. Для решения проблем, связанных с децентрализацией отключенных объектов, нужно заранее выделить контейнер для объектов одного типа. Например, для окон и диалогов стоит выделить контейнер, куда будут помещаться все окна и диалоги при их инициализации, которые будут существовать только в рамках одного родительского объекта.
Третей проблемой, с которой чаще всего сталкиваются начинающие разработчики на Unity, - это злоупотребление паттерном Singleton [7]. Данный паттерн позволяет создать в проекте класс, который гарантирует, что он будет иметь единственный экземпляр во всём проекте (см. Листинг 5).
Листинг 5. Инициализация класса как Singleton using UnityEngine; public class Test : MonoBehaviour { public static Test Instance; public int Score {get; private set;} private void Start() { If (Instance == null) { Instance = this; } else { Destroy(gameObject); } } }После создания экземпляра появляется возможность получить доступ к данному классу из любой точки проекта (см. Листинг 6).
Листинг 6. Получение доступа к Singleton
using UnityEngine;
public class Test2 : MonoBehaviour
{
private void SomeAction()
{
Test.Instance.Score++; } }Данным подходом чаще всего злоупотребляют новички в программировании, которые начали своё обучение с Unity. Плюсом данного подхода является скорость реализации на ранних этапах создания проекта, а также простота его применения. Минусы же гораздо более весомые в долгосрочной перспективе. Чем больше разрастается проект, тем менее управляемым он становится, так как Singleton порождает большое количество связей в проекте, а также сводит к минимуму контроль над вызовом его методов, так как его методы может вызывать любой класс в проекте. Вместо использования Singleton лучше уделить больше времени на построение архитектуры приложения и применить паттерны проектирования, которые не связывают все объекты с одним классом. Паттерн Singleton может быть очень полезен для прототипирования приложения (так как цикл жизни прототипа очень низкий, а поддержка отсутствует), либо для централизации событий, о которых говорилось ранее.
Заключение
Unity является мощным инструментом, благодаря которому можно создавать большие приложения со сложной иерархией, поэтому при работе с ним необходимо уделить особое внимание проработке и построению архитектуры приложения. Учитывая описанные проблемы можно снизить риски, связанные с неуправляемостью кода, а также улучшить производительность за счёт грамотного проектирования и управления сценой.
Конфликт интересов Не указан. | Conflict of Interest None declared. |
Список литературы / References
- Бочкарев Н.А. Подходы к трансформации объектов виртуальных пространств в среде Unity / Н.А. Бочкарев, Р.С. Молотов // Вестник Ульяновского государственного технического университета. – 2016. – №3. – С.38-41.
- Зенг, В.А. Создание прототипа компьютерного бесконтактного компьютерного интерфейса в Unity 3D / В.А. Зенг // Известия Тульского государственного университета. Технические науки. – 2019. – Выпуск 12. – С. 480-485.
- Калиниченко А.В. Интерактивные электронные дидактические средства с когнитивной визуализацией / А.В. Калиниченко // Научно-технический вестник информационных технологий, механики и оптики. –2017. Т. 17. – № 2. – С. 359–364. DOI: 10.17586/2226- 1494-2017-17-2-359-364
- Матвеев П.О. Особенности моделирования светотехники и звуковых эффектов транспортных средств при разработке обучающих симуляторов в среде Unity / П.О. Матвеев, Р.С. Молотов // Вестник Ульяновского государственного технического университета. – 2016. – № 3. – С. 48-52.
- Сабанчиев А.М. Система визуализации с использованием виртуальной реальности в комплексе симуляции полета / А.М. Сабанчиев, Т.И. Кулиев // Электротехнические и информационные комплексы и системы. – 2018. Т.14. – № 4. – С.80-86. DOI: 10.17122/1999-5458-2018-14-4-80-86
- Тришин И.Г. Опыт создания программного обеспечения на базе игрового движка Unity 3D для решения задач реконструкции фасадов Георгиевского собора г. Юрьев-Польский (Владимирская область) [Электронный ресурс] / И.Г. Тришин // Историческая информатика. – 2018. – № 2. – С. 68 - 74. – URL: https://nbpublish.com/library_read_article.php?id=26602. DOI: 10.7256/2585-7797.2018.2.26602
- Hipple R. Three ways to architect your game with ScriptableObjects [Электронный ресурс] / R. Hipple. – URL: https://unity.com/how-to/architect-game-code-scriptable-objects (accessed: 12.02.2020)
- Izzo S. Type-safe object pool for Unity [Электронный ресурс] / S. Izzo. – URL: https://www.gamasutra.com/blogs/SamIzzo/20180611/319671/Typesafe_object_pool_for_Unity.php (accessed: 02.2020)
- Placzek M. Object pooling in Unity [Электронный ресурс] / M. Placzek. – URL: https://www.raywenderlich.com/847-object-pooling-in-unity (accessed: 12.02.2020)
- Simonov V. 10000 Update() calls [Электронный ресурс] / V. Simonov. – URL: https://blogs.unity3d.com/2015/12/23/1k-update-calls (accessed: 12.02.2020)
Список литературы на английском языке / References in English
- Bochkarev N.A. Podhody k transformacii ob"ektov virtual'nyh prostranstv v srede Unity [Approaches to the transformation of objects of virtual spaces in a Unity environment] / N.A. Bochkarev, R.S. Molotov // Vestnik Ul'yanovskogo gosudarstvennogo tekhnicheskogo universiteta [Bulletin of the Ulyanovsk State Technical University]. – 2016. – №3. – pp. 38-41. [in Russian].
- Zeng V.A. Sozdanie prototipa komp'yuternogo beskontaktnogo komp'yuternogo interfejsa v Unity 3D [Prototyping a computer contactless computer interface in Unity 3D] / V.A. Zeng // Izvestiya Tul'skogo gosudarstvennogo universiteta. Tekhnicheskie nauki [Bulletin of Tula State University. Technical science]. – 2019. – Vol.12. – pp. 480-485. [in Russian]
- Kalinichenko A.V. Interaktivnye elektronnye didakticheskie sredstva s kognitivnoj vizualizaciej [Interactive electronic didactic tools with cognitive visualization] / A.V. Kalinichenko // Nauchno-tekhnicheskij vestnik informacionnyh tekhnologij, mekhaniki i optiki [Scientific and technical bulletin of information technologies, mechanics and optics]. –2017. – № 2. – pp. 359–364. DOI: 10.17586/2226- 1494-2017-17-2-359-364 [in Russian].
- Matveev P.O. Osobennosti modelirovaniya svetotekhniki i zvukovyh effektov transportnyh sredstv pri razrabotke obuchayushchih simulyatorov v srede Unity [Features of modeling lighting and sound effects of vehicles in the development of training simulators in a Unity environment] / P.O. Matveev, R.S. Molotov // Vestnik Ul'yanovskogo gosudarstvennogo tekhnicheskogo universiteta [Bulletin of the Ulyanovsk State Technical University]. – 2016. – № 3. – pp. 48-52. [in Russian]
- Sabanchiev A.M. Sistema vizualizacii s ispol'zovaniem virtual'noj real'nosti v komplekse simulyacii poleta [Visualization system using virtual reality in a flight simulation complex] / A.M. Sabanchiev, T.I. Kuliev // Elektrotekhnicheskie i informacionnye kompleksy i sistemy [Electrical and information systems and systems]. – 2018.– № 4. – pp. 80-86. DOI: 10.17122/1999-5458-2018-14-4-80-86 [in Russian].
- Trishin I.G. Opyt sozdaniya programmnogo obespecheniya na baze igrovogo dvizhka Unity 3D dlya resheniya zadach rekonstrukcii fasadov Georgievskogo sobora g. YUr'ev-Pol'skij (Vladimirskaya oblast') [The experience of creating software based on the Unity 3D game engine for solving the problems of reconstruction of the facades of St. George Cathedral in Yuryev-Polsky (Vladimir Region)] [Electronic resource] / I.G. Trishin // Istoricheskaya informatika [Historical computer science]. – 2018. – № 2. – pp. 68 - 74. – URL: https://nbpublish.com/library_read_article.php?id=26602. DOI: 10.7256/2585-7797.2018.2.26602 (accessed: 12.02.2020) [in Russian].
- Hipple R. Three ways to architect your game with ScriptableObjects [Electronic resource] / R. Hipple. – URL: https://unity.com/how-to/architect-game-code-scriptable-objects (accessed: 12.02.2020).
- Izzo S. Type-safe object pool for Unity [Electronic resource] / S. Izzo. – URL: https://www.gamasutra.com/blogs/SamIzzo/20180611/319671/Typesafe_object_pool_for_Unity.php (accessed: 12.02.2020)
- Placzek M. Object pooling in Unity [Electronic resource] / M. Placzek. –URL: https://www.raywenderlich.com/847-object-pooling-in-unity (accessed: 12.02.2020).
- Simonov V. 10000 Update calls [Electronic resource] / V. Simonov. – URL: https://blogs.unity3d.com/2015/12/23/1k-update-calls (accessed: 12.02.2020).