6 способов, которыми ScriptableObjects могут принести пользу вашей команде и вашему коду

THOMAS KROGH-JACOBSEN / UNITY TECHNOLOGIESProduct Marketing Core Tech
Apr 20, 2023|17 Мин
6 способов, которыми ScriptableObjects могут принести пользу вашей команде и вашему коду
Эта веб-страница была переведена с помощью машинного перевода для вашего удобства. Мы не можем гарантировать точность или надежность переведенного контента. Если у вас есть вопросы о точности переведенного контента, обращайтесь к официальной английской версии веб-страницы.

Мы рады сообщить, что запустили новую техническую электронную книгу, Создание модульной игровой архитектуры в Unity с помощью ScriptableObjectsв которой собраны лучшие практики профессиональных разработчиков по внедрению ScriptableObjects в производство.

Вместе с электронной книгой вы можете загрузить с GitHub демонстрационный проект, вдохновленный классической механикой аркадных игр с мячом и веслом. Демонстрация показывает, как ScriptableObjects может помочь вам создать компоненты, которые поддаются тестированию и масштабированию, а также удобны для дизайнеров. Хотя подобную игру можно было бы создать с гораздо меньшим количеством строк кода, эта демонстрация показывает ScriptableObjects в действии.

Электронная книга сопровождается демонстрацией мяча и весла.
Электронная книга сопровождается демонстрацией мяча и весла.

В этом посте рассказывается о преимуществах ScriptableObjects, но не рассматриваются основы и общее кодирование в Unity. Если вы новичок в программировании в Unity, загляните на сайт Unity Learn, где представлены полезные вводные уроки. Первая глава электронной книги также представляет собой солидный учебник.

Давайте рассмотрим шесть способов, с помощью которых вы можете получить выгоду от использования ScriptableObjects в своих проектах. Хотите узнать больше? Все эти примеры рассматриваются в электронной книге и демо-проекте.

1. Эффективное сотрудничество в команде

Хотя многие из описанных здесь приемов можно реализовать и с помощью классов C#, одним из главных преимуществ ScriptableObjects является их доступность для художников и дизайнеров. Они могут использовать ScriptableObjects для настройки и применения игровой логики в проекте без необходимости редактировать код.

Редактор позволяет удобно просматривать и редактировать объекты ScriptableObjects, позволяя дизайнерам настраивать данные геймплея без серьезной поддержки со стороны команды разработчиков. Это также относится к игровой логике, например, к применению поведения NPC путем добавления объекта ScriptableObject (объясняется в паттернах ниже).

Хранение данных и логики в одном MonoBehaviour может привести к трудоемким конфликтам слияния, если два человека изменяют разные части одного и того же префаба или сцены. Разбивая общие данные на более мелкие файлы и активы с помощью ScriptableObjects, дизайнеры могут строить геймплей параллельно с разработчиками, а не ждать, пока последние закончат настройку геймплея в коде, прежде чем тестировать его.

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

Узнайте, как дизайнер игр использует объекты ScriptableObjects

Кристо Ноббс, старший технический гейм-дизайнер, специализирующийся на разработке системных игр и Unity (C#), принял участие в написании книги The Unity game designer playbookи является основным автором серии статей в блоге о проектировании игровых систем в Unity. Его посты: "Системы, создающие экосистемы: Эмерджентный игровой дизайн" и "Непредсказуемо весело: Значение рандомизации в дизайне игр" приводят интересные примеры того, как дизайнеры могут использовать ScriptableObjects.

2. Контейнеры данных

Модульность - это общий принцип программного обеспечения, который может быть реализован в C# без использования ScriptableObjects. Но, как уже говорилось выше, ScriptableObjects способствуют чистоте кодирования, отделяя данные от логики, что является первым шагом к модульному игровому коду. Такое разделение облегчает внесение изменений, не вызывая непредвиденных побочных эффектов, и улучшает тестируемость.

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

Хотя изменения в данных ScriptableObject сохраняются в редакторе, важно отметить, что они не предназначены для сохранения игровых данных. В этом случае лучше использовать систему сериализации, например JSON, XML, или бинарное решение, если производительность имеет решающее значение.

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

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

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

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

3. Перечисления

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

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

Перетаскивание категорий на основе объектов ScriptableObject
Перетаскивание категорий на основе объектов ScriptableObject

Использование ScriptableObjects в качестве перечислений становится более интересным, когда вы хотите расширить их и добавить больше данных. В отличие от обычных перечислений, ScriptableObjects могут иметь дополнительные поля и методы. Нет необходимости в отдельной таблице поиска или соотнесении с новым массивом данных.

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

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

Предположим, вы хотите сделать предмет экипируемым в ролевой игре. Для этого можно добавить к объекту ScriptableObject дополнительное поле boolean. Некоторым персонажам не разрешается держать определенные предметы? Являются ли некоторые предметы магическими или обладают ли они особыми способностями? Это могут сделать перечисления на основе ScriptableObject.

4. Делегатские объекты

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

Если вам нужно выполнить конкретные задачи, вы можете инкапсулировать их алгоритмы в собственные объекты. В оригинальной "Банде четырех" этот общий дизайн называется " паттерн стратегии". В примере ниже показано, как сделать паттерн стратегии более полезным, используя абстрактный класс для реализации EnemyAI. В результате получается несколько производных ScriptableObjects с различным поведением, которое затем становится подключаемым поведением, поскольку каждый актив является взаимозаменяемым. Вы просто перетаскиваете выбранный объект ScriptableObject в MonoBehaviour.

Подключаемые поведения могут изменяться во время выполнения или в редакторе.
Подключаемое поведение может изменяться во время выполнения или в редакторе

Подробный пример использования ScriptableObjects для управления поведением можно посмотреть в видеосерии Pluggable AI with ScriptableObjects. На этих занятиях демонстрируется система ИИ на основе конечного автомата состояния, которая может быть настроена с помощью объектов ScriptableObjects для состояний, действий и переходов между этими состояниями.

5. Каналы событий

Частой проблемой в больших проектах является необходимость обмена данными или состояниями между несколькими игровыми объектами, что позволяет избежать прямых ссылок между этими объектами. Управление этими зависимостями в масштабе может потребовать значительных усилий и часто является источником ошибок. Многие разработчики используют синглтоны - один глобальный экземпляр класса, который переживает загрузку сцены. Однако синглтоны вводят глобальные состояния и затрудняют модульное тестирование. Если вы работаете с префабом, который ссылается на синглтон, вам придется импортировать все его зависимости только для того, чтобы протестировать изолированную функцию. Это делает ваш код менее модульным и эффективным для отладки.

Одно из решений - использовать события на основе ScriptableObject, чтобы помочь вашим игровым объектам взаимодействовать. В данном случае вы используете ScriptableObjects для реализации одной из форм паттерна наблюдателя, когда субъект передает сообщение одному или нескольким слабо развязанным наблюдателям. Каждый наблюдающий объект может реагировать независимо от субъекта, но не знает о других наблюдателях. Субъект также может называться "издателем" или "вещателем", а наблюдатели - "подписчиками" или "слушателями".

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

Канал событий на основе ScriptableObject
Канал событий на основе ScriptableObject

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

Легкий постоянный доступ к определенным ресурсам - вот почему многие разработчики используют синглтоны. ScriptableObjects часто могут обеспечить те же преимущества, не создавая столько ненужных зависимостей.

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

Один из способов представить себе это как "канал событий". Представьте ScriptableObject как радиовышку, на которой находится любое количество объектов, слушающих ее сигналы. Заинтересованный MonoBehaviour может подписаться на канал событий и реагировать, когда что-то происходит.

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

6. Наборы времени выполнения

Во время выполнения вам часто нужно отслеживать список игровых объектов или компонентов в сцене. Например, список врагов - это то, к чему вам нужно часто обращаться, но это также динамический список, который меняется по мере появления новых врагов или их поражения. Синглтон обеспечивает легкий доступ к глобальным объектам, но у него есть несколько недостатков. Вместо того чтобы использовать синглтон, подумайте о хранении данных в ScriptableObject в виде "Runtime Set". Экземпляр ScriptableObject появляется на уровне проекта, что означает, что он может хранить данные, которые доступны любому объекту из любой сцены, предлагая аналогичный глобальный доступ. Поскольку данные находятся на активе, открытый список элементов доступен в любое время.

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

Набор Runtime Set обеспечивает глобальный доступ к коллекции данных.
Набор времени выполнения обеспечивает глобальный доступ к коллекции данных

Чтение данных непосредственно из ScriptableObject также более оптимально, чем поиск в иерархии сцены с помощью операций find, таких как Object.FindObjectOfType или GameObject.FindWithTag. В зависимости от сценария использования и размера иерархии, это относительно дорогие методы, которые могут быть неэффективны для покадрового обновления.

Существует несколько фреймворков ScriptableObjects, которые предлагают больше вариантов использования, чем эти шесть сценариев. Некоторые команды решают широко использовать ScriptableObjects, в то время как другие ограничивают их применение загрузкой статических данных и отделением логики от данных. В конечном счете, потребности вашего проекта определят, как вы будете их использовать.

Продвинутая серия для программистов Unity

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

Создайте руководство по стилю C#: Пишите более чистый код, который масштабируется поможет вам разработать руководство по стилю, которое поможет унифицировать ваш подход к созданию более целостной кодовой базы.

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

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

Более подробные руководства и статьи можно найти в хабе "Лучшие практики Unity".

Обложка электронной книги "Создание модульной архитектуры игр в Unity с помощью ScriptableObjects"
Электронная книга "Создание модульной игровой архитектуры в Unity с помощью ScriptableObjects"