Hero background image
Использование объектов ScriptableObjects в качестве объектов делегатов

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

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

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

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

В эту серию вошли следующие статьи:

Важное замечание перед началом работы

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

У каждой модели есть свои плюсы и минусы. Выбирайте только те, которые приносят значительную пользу вашему конкретному проекту. Ваши дизайнеры в значительной степени полагаются на редактор Unity? Шаблон на основе ScriptableObject может стать хорошим выбором, чтобы помочь им сотрудничать с вашими разработчиками.

В конечном счете, лучшая архитектура кода - это та, которая подходит вашему проекту и команде.

ScriptableObjects
SCRIPTABLEOBJECT СОДЕРЖИТ "СТРАТЕГИЮ" В РАМКАХ МОНОПОВЕДЕНИЯ.
Модель стратегии

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

Одно из применений - инкапсулировать алгоритмы выполнения определенных задач в ScriptableObject, а затем использовать этот ScriptableObject в контексте чего-то другого.

Например, если вы пишете ИИ или систему поиска пути для класса EnemyUnit, вы можете создать объект ScriptableObject с техникой поиска пути (например, A*, Dijkstra и т. д.).

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

Когда MonoBehaviour нужно выполнить задачу, он вызывает внешние методы объекта ScriptableObject, а не свои собственные. Например, объект ScriptableObject может содержать публичные методы MoveUnit или SetTarget для управления вражеским юнитом и указания места назначения.

Подключаемое поведение

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

Если вам нужно, чтобы EnemyUnit менял поведение в зависимости от условий игры, внешний контекст (MonoBehaviour) может проверить эти условия. Затем в качестве ответа он может подключить другой объект ScriptableObject.

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

Чтобы создать это подключаемое поведение, убедитесь, что:

  • Определите базовый класс или интерфейс для стратегии: Этот класс или интерфейс должен включать методы и свойства, необходимые для выполнения стратегии.
  • Создайте классы ScriptableObject: Каждый из них может обеспечить различные варианты реализации стратегии. Например, вы можете создать один класс, реализующий простой алгоритм искусственного интеллекта, и другой класс, реализующий более сложный алгоритм.
  • Создайте объект ScriptableObject, реализующий стратегию: Заполните недостающую логику и введите в Инспектор все необходимые значения.
  • Используйте стратегию в контексте: В MonoBehaviour вызовите методы и свойства, реализованные в ScriptableObject. Чтобы упростить вызов этих методов, передайте зависимости в качестве параметров.

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

Пример: AudioDelegate

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

Абстрактный класс AudioDelegateSO определяет единственный метод Play, который принимает параметр AudioSource. Конкретная реализация затем переопределяет это.

Подкласс SimpleAudioDelegateSO определяет массив AudioClips. Он выбирает случайный клип и воспроизводит его с помощью переопределенной реализации метода Play. Это добавляет изменение высоты тона и громкости в заданном диапазоне.

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

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

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

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

Делегатские объекты
ДЕМОНСТРАЦИОННАЯ СЦЕНА AUDIODELEGATES ПОКАЗЫВАЕТ, КАК СКРИПТОВЫЕ ОБЪЕКТЫ МОГУТ СОДЕРЖАТЬ ЛОГИКУ.
Демонстрация узоров

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

В этом примере каждый угол включает в себя источник аудиосигнала. Моноповедение AudioModifier использует делегат на основе объекта ScriptableObject для воспроизведения звука.

Разница в высоте тона обусловлена только настройками каждого актива ScriptableObject (BeepHighPitched_SO, BeepLowPitched_SO и т. д.).

Использование объекта ScriptableObject для управления логикой действий может облегчить команде дизайнеров эксперименты с идеями. Это позволяет дизайнеру работать более независимо от разработчика.

Целевой менеджер
МОНОПОВЕДЕНИЕ OBJECTIVEMANAGER ПРОВЕРЯЕТ УСЛОВИЯ ПОБЕДЫ С ПОМОЩЬЮ СКРИПТОВЫХ ОБЪЕКТОВ.
Пример: Модель стратегии в системе целей

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

Абстрактный базовый класс ObjectiveSO содержит такие значения, как название цели и то, была ли она выполнена.

Конкретные подклассы, например ScoreObjectiveSO, реализуют логику выполнения каждой цели. Для этого нужно переопределить метод ObjectiveSO's CompleteObjective и добавить логику условия победы.

Нужно ли игроку набрать определенное количество очков или победить определенное количество врагов? Нужно ли им добраться до определенного места или забрать конкретный предмет? Это обычные условия победы, которые могут стать целями на основе ScriptableObject.

ObjectiveManager служит большим контекстом для объектов ScriptableObjects. Он ведет список ObjectiveSOs и полагается на каждый ScriptableObject, чтобы определить, завершен ли он. Когда все ObjectiveSO покажут состояние завершения, игра закончится.

Например, ScoreObjectiveSO показывает один из способов реализации цели подсчета очков:

  • Пользовательская структура PlayerScore соответствует идентификатору игрока, элементу пользовательского интерфейса и фактическому значению счета.
  • Каждый раз, когда компонент ScoreManager обновляется, цель проверяет условие победы.
  • Если результат игрока соответствует или превышает m_TargetScore, то в качестве события отправляется объект PlayerScore победителя.

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

Опять же, цель здесь - модульность. Это позволяет настраивать каждый ObjectiveSO, не затрагивая уже существующие.

У игры PaddleBallSO есть только одна цель. Если один из игроков достигнет победной цели, игра закончится.

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

Поскольку логика инкапсулирована в ScriptableObject, вы можете поменять любой ObjectiveSO на другой. Чтобы создать новое условие победы, нужно просто изменить список в ObjectiveManager. В некотором смысле цель "подключается" к окружающему контексту.

Обратите внимание, что одним из удобных аспектов ObjectiveSO является событие, используемое для отправки сообщений между игровыми объектами. Далее мы рассмотрим, как использовать ScriptableObjects для реализации этой событийно-ориентированной архитектуры.

скриптовое аутро
Другие ресурсы ScriptableObject

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

Понравился ли вам этот контент?