Эта страница объясняет, как использовать ScriptableObjects в качестве контейнеров данных, которые отделяют данные от логики в коде вашей игры.
Это второе из серии шести мини-руководств, созданных для того, чтобы помочь разработчикам Unity с демонстрация которая сопровождает электронную книгу " Создание модульной игровой архитектуры в Unity с помощью ScriptableObjects".
Демонстрация вдохновлена классической механикой аркадных игр с мячом и веслом и показывает, как ScriptableObjects может помочь вам создать компоненты, которые поддаются тестированию, масштабируются и удобны для дизайнеров.
Вместе электронная книга, демонстрационный проект и эти мини-руководства предоставляют лучшие практики использования шаблоны проектирования с классом ScriptableObject в вашем проекте Unity. Эти советы помогут вам упростить код, уменьшить потребление памяти и способствовать повторному использованию кода.
В эту серию вошли следующие статьи:
- Начните работу с демонстрацией Unity ScriptableObjects
- Использование перечислений на основе ScriptableObject в проекте Unity
- Использование объектов ScriptableObjects в качестве объектов делегатов
- Использование ScriptableObjects в качестве каналов событий в игровом коде
- Как использовать набор времени выполнения на основе ScriptableObject
Прежде чем вы погрузитесь в демонстрационный проект ScriptableObject и эту серию мини-руководств, помните, что в своей основе паттерны проектирования - это всего лишь идеи. Они применимы не ко всем ситуациям. Эти приемы помогут вам освоить новые способы работы с Unity и ScriptableObjects.
У каждой модели есть свои плюсы и минусы. Выбирайте только те, которые приносят значительную пользу вашему конкретному проекту. Ваши дизайнеры в значительной степени полагаются на редактор Unity? Шаблон на основе ScriptableObject может стать хорошим выбором, чтобы помочь им сотрудничать с вашими разработчиками.
В конечном счете, лучшая архитектура кода - это та, которая подходит вашему проекту и команде.
Разработчики программного обеспечения часто заботятся о модульности - разбиении приложения на более мелкие, самодостаточные части. Каждый модуль отвечает за определенный аспект функциональности приложения.
В Unity объекты ScriptableObjects помогают отделить данные от логики.
Объекты ScriptableObjects отлично справляются с хранением данных, особенно если они статичны. Это делает их идеальными для игровой статистики, значений конфигурации предметов или NPC, диалогов персонажей и многого другого.
Изолируя данные геймплея от логики поведения, можно упростить тестирование и поддержку каждой независимой части проекта. Такое "разделение забот" поможет уменьшить непредвиденные и нежелательные побочные эффекты при внесении необходимых изменений.
Если вы хотите узнать больше о рабочем процессе ScriptableObject, вам поможет эта статья в Unity Learn. В противном случае, вот краткое объяснение:
Определите объект ScriptableObject: Чтобы создать такой класс, определите класс C#, который наследуется от базового класса ScriptableObject, с полями и свойствами для данных, которые вы хотите хранить. ScriptableObjects могут хранить те же типы данных, что и MonoBehaviours, что делает их универсальными контейнерами данных. Добавьте атрибут CreateAssetMenuAttribute из редактора, чтобы упростить создание актива в проекте.
Создайте актив: Определив класс ScriptableObject, вы можете создать экземпляр этого ScriptableObject в проекте. Это выглядит как актив, сохраненный на диске, который можно использовать в разных игровых объектах и сценах.
Установите значения: После создания актива заполните его данными, задав значения его полей и свойств в Инспекторе.
Используйте актив: Когда актив содержит данные, ссылайтесь на него из переменной или поля. Любые изменения, внесенные в актив ScriptableObject, будут отражены во всем проекте.
Вы можете использовать ScriptableObjects в качестве контейнеров данных в разных частях вашей игры. Например, вы можете определить свойства оружия или персонажа в ScriptableObject, а затем ссылаться на этот актив из любой точки проекта.
Примечание. Вы также можете генерировать ScriptableObjects во время выполнения программы с помощью метода CreateInstance. Однако для хранения данных вы обычно создаете активы ScriptableObject заранее с помощью атрибута CreateAssetMenuAttribute.
Чтобы лучше понять, почему ScriptableObjects - более подходящий выбор для хранения данных, чем MonoBehaviours, сравните пустые версии каждого из них. Убедитесь, что для сериализации активов установлен режим : Принудительный текст в настройках проекта, чтобы просматривать разметку YAML в виде текста.
Создайте новый объект GameObject с пустым MonoBehaviour. Затем сравните его с пустым активом ScriptableObject. Поместите их рядом друг с другом, и они должны выглядеть так, как показано на рисунке выше.
ScriptableObjects легче, чем MonoBehaviours, и не несут накладных расходов, связанных с последними, например, компонента Transform. Благодаря этому ScriptableObjects занимают меньше места в памяти и более оптимизированы для хранения данных.
ScriptableObjects сохраняются как активы, поэтому они сохраняются и вне режима игры, что может быть полезно. Например, данные ScriptableObject доступны из любого места, даже если вы загружаете новую сцену.
В демонстрационном примере Patterns представлен базовый экран кредитов, который вы можете протестировать самостоятельно. Измените объект Credits_Data ScriptableObject, а затем нажмите Update, чтобы увидеть появление сохраненного текста.
Если у вас есть ролевая игра с большим количеством диалогов или обучающая сцена с заранее подготовленным сценарием, это обычный способ хранения большого количества данных.
Хотя данные внутри объекта ScriptableObject обновляются мгновенно при изменении, в нашем проекте требуется кнопка Update, чтобы обновить экран вручную. Экран на основе UI Toolkit создается только один раз и нуждается в уведомлении о том, что данные были изменены.
Создайте событие внутри объекта ScriptableObject, если вы хотите синхронизировать обновления автоматически. Например, этот сценарий ExampleSO будет вызывать событие OnValueChanged при каждом изменении ExampleValue. Посмотрите на пример кода ниже.
Затем пусть ваш прослушивающий объект пользовательского интерфейса подпишется на OnValueChanged и обновится соответствующим образом.
ScriptableObjects - это то, что нужно, когда многие объекты используют одни и те же данные. Например, если вы создаете стратегическую игру, в которой множество юнитов имеют одинаковую скорость атаки и максимальное здоровье, неэффективно хранить эти значения отдельно для каждого объекта GameObject.
Вместо этого можно консолидировать общие данные в центральном месте и сделать так, чтобы каждый объект ссылался на это общее место. В разработке программного обеспечения это оптимизация, известная как паттерн flyweight. Такая реструктуризация кода позволяет избежать копирования большого количества значений и сократить объем занимаемой памяти.
В PaddleBallSO в качестве общего хранилища данных выступает объект сценария GameDataSO.
Вместо того чтобы хранить отдельные копии общих настроек (скорость, масса, физика отскока и т. д.), скрипты Paddle и Ball по возможности ссылаются на один и тот же экземпляр GameDataSO. Каждый игровой элемент хранит уникальные данные, такие как позиции и события ввода, но по умолчанию использует общие данные, когда это возможно.
Хотя экономия памяти может быть незаметна при использовании всего двух или трех объектов, редактирование общих данных происходит быстрее и с меньшим количеством ошибок, чем редактирование каждого из них вручную.
Например, если вам нужно изменить скорость вращения весла, регулировка в одном месте обновляет оба весла на всех сценах. Если вы хранили их как уникальные поля в MonoBehaviours, один неверный щелчок может легко привести к рассинхронизации двух значений.
Выгрузка данных в ScriptableObjects также может помочь в контроле версий и предотвратить конфликты слияния, когда члены команды работают над одной и той же сценой или префабом.
GameDataSO показывает, как использовать ScriptableObject в качестве контейнера данных. В PaddleBallSO это включает в себя различные настройки для конфигурации игрового процесса:
- Данные весла: Такие атрибуты, как скорость, сопротивление и масса весла, определяют движение и физику весла во время игры.
- Данные о мяче: Здесь хранятся текущая скорость мяча, его максимальная скорость и множитель отскока, который управляет поведением мяча при взаимодействии с симуляцией.
- Данные матча: GameDataSO содержит информацию о задержках между точками во время матча, помогая контролировать темп игры.
- Идентификаторы игроков: Объекты сценария PlayerIDSO выполняют функцию идентификации команды для каждого игрока (например, Player1 и Player2).
- Спрайты игроков: Эти дополнительные спрайты позволяют настраивать аватар игрока.
- Планировка уровня: Объект LevelLayoutSO определяет начальные позиции для игроков и игровых элементов, таких как ворота и стены.
GameDataSO позволяет любому объекту использовать все эти настройки и данные в центральном хранилище. Это упрощает управление этими объектами и способствует большей последовательности во всем проекте. Изменить физику весла? Внесите одно изменение здесь вместо того, чтобы настраивать несколько скриптов.
Иногда можно не только съесть пирог, но и получить его. С помощью двойной сериализации вы можете хранить данные в ScriptableObject, одновременно сохраняя их в другом формате.
Скрипт LevelLayoutSO демонстрирует эту концепцию. В дополнение к хранению начальных позиций для лопастей и мяча, он хранит данные о преобразовании стен и ворот в пользовательской структуре.
Эти значения могут быть записаны на диск с помощью метода ExportToJson. JSON-файлы представляют собой человекочитаемый текст, что позволяет легко изменять их вне Unity. Это позволяет работать с объектами ScriptableObjects в редакторе, а затем хранить их данные в другом месте, например, в файле JSON или XML.
Такие форматы файлов, как JSON и XML, могут быть сложны для работы в редакторе, но их легко изменить вне Unity в текстовом редакторе. Это открывает возможности для создания пользовательских или модифицированных уровней.
Сценарий GameSetup может использовать либо объект LevelLayout ScriptableObject, либо внешний JSON-файл для создания игрового уровня.
Для загрузки пользовательского модифицированного уровня сценарий установки создает объект ScriptableObject во время выполнения с помощью CreateInstance. Затем он считывает текст из файла JSON, чтобы заполнить объект ScriptableObject.
Ваши пользовательские данные заменяют содержимое объекта ScriptableObject и позволяют вам использовать этот внешний модифицированный уровень как любой другой. Остальные приложения работают нормально, не подозревая о переключении.
Хотя наша мини-игра с мячом не может продемонстрировать все варианты использования контейнеров данных ScriptableObject, рассмотрите следующие варианты для ваших собственных приложений:
- Конфигурация игры: Подумайте о константах, правилах игры или любых других параметрах настройки, которые не должны меняться в процессе игры. Другие компоненты могут обращаться к этим данным конфигурации без использования жестко заданных значений.
- Атрибуты персонажей и врагов: Используйте объекты ScriptableObjects для определения таких атрибутов, как здоровье, сила атаки, скорость и т.д. Это позволит вашим дизайнерам балансировать и настраивать элементы геймплея без участия разработчика.
- Системы инвентаризации и товаров: Определения и свойства элементов, такие как имена, описания и значки, идеально подходят для ScriptableObjects. Вы также можете использовать их как часть системы управления инвентарем, чтобы отслеживать предметы, которые игрок собирает, использует или экипирует.
- Диалог и нарративные системы: Объекты ScriptableObjects могут хранить текст диалогов, имена персонажей, разветвленные пути диалогов и другие данные, связанные с повествованием. Они могут стать основой для создания сложных диалоговых систем.
- Данные об уровне и прогрессе: С помощью ScriptableObjects можно определять планировку уровня, точки появления врагов, цели и другую информацию, связанную с уровнем.
- Аудиоклипы: Как показано в проекте PaddleBallSO, объекты ScriptableObjects могут хранить один или несколько аудиоклипов. С их помощью можно задать аудиоэффекты или музыку для нескольких частей игры.
- Анимационные клипы: ScriptableObjects можно использовать для хранения клипов анимации, что полезно для определения общих анимаций, которые используются несколькими игровыми объектами или персонажами.
По мере того как вы будете углубляться в ScriptableObjects и адаптировать их к своим собственным проектам, вы откроете для них еще больше возможностей. Они особенно полезны для управления данными и позволяют поддерживать согласованность различных элементов игры.
Подробнее о паттернах проектирования с помощью ScriptableObjects читайте в электронной книге Создание модульной игровой архитектуры в Unity с помощью ScriptableObjects. Вы также можете узнать больше о распространенных паттернах разработки Unity в Повысьте уровень своего кода с помощью паттернов программирования игр.