
Используйте перечисления на основе ScriptableObject в вашем проекте Unity
Эта страница объясняет, как использовать перечисления на основе ScriptableObject в вашем проекте Unity.
Это третья часть серии из шести мини-руководств, созданных для помощи разработчикам Unity с демонстрацией, которая сопровождает электронную книгу, Создайте модульную игровую архитектуру в Unity с помощью ScriptableObjects.
Демонстрация вдохновлена классической механикой аркадных игр с мячом и ракеткой и показывает, как ScriptableObjects могут помочь вам создать компоненты, которые можно тестировать, масштабировать и которые удобны для дизайнеров.
Вместе электронная книга, демонстрационный проект и эти мини-руководства предоставляют лучшие практики для использования шаблонов проектирования программирования с классом ScriptableObject в вашем проекте Unity. Эти советы могут помочь вам упростить ваш код, уменьшить использование памяти и способствовать повторному использованию кода.
Эта серия включает в себя следующие статьи:
- Начните с демонстрации ScriptableObjects в Unity
- Отделите игровые данные и логику с помощью ScriptableObjects
- Используйте ScriptableObjects в качестве делегатных объектов
- Используйте ScriptableObjects в качестве каналов событий в игровом коде
- Как использовать наборы времени выполнения на основе ScriptableObject
Важное примечание перед началом
Прежде чем погрузиться в демонстрационный проект ScriptableObject и эту серию мини-руководств, помните, что в своей основе шаблоны проектирования — это просто идеи. Они не применимы ко всем ситуациям. Эти техники могут помочь вам узнать новые способы работы с Unity и ScriptableObjects.
Каждый шаблон имеет свои плюсы и минусы. Выбирайте только те, которые действительно приносят пользу вашему конкретному проекту. Ваши дизайнеры сильно полагаются на Unity Editor? Шаблон на основе ScriptableObject может быть хорошим выбором, чтобы помочь им сотрудничать с вашими разработчиками.
В конечном итоге, лучшая архитектура кода — это та, которая подходит вашему проекту и команде.

Расширяемые перечисления
Enums — это удобный способ управлять фиксированным набором именованных значений в вашем коде. Однако у них есть некоторые ограничения. Поскольку сериализованные значения enum хранятся как целые числа, а не их символьные имена, удаление или переупорядочивание значения может привести к неправильному или неожиданному поведению. Это означает, что enums, особенно когда у вас их много, могут создавать головную боль в разработке Unity.
Стандартный подход
Вот как выглядит типичный enum:
[System.Serializable]
public enum HandGestures
{
Камень,
Бумага,
Ножницы
}
Вы можете сериализовать перечисление с атрибутом System.Serializable и оно будет отображаться в инспекторе.
Проблема
Изменение порядка или удаление значения может вызвать проблемы. Поскольку каждое значение внутренне является целым числом, то то, что оно представляет, может стать чем-то другим. В данном примере удаление значения Бумага приведет к тому, что Ножницы примут значение 1.
Или, если мы добавим значение, как в примере ниже.
Выбранное значение перечисления изменится, если оно идет после удаленного элемента.
Это может вызвать проблемы при поддержке и обновлении проектов, особенно когда ваше перечисление содержит множество значений. Вы можете смягчить эту проблему, оставив пустой или неиспользуемый элемент, или явно установив целочисленные значения. Тем не менее, ни одно из решений не является идеальным.

Перечисления на основе ScriptableObject
Перечисления на основе ScriptableObject предоставляют вам функциональность традиционных перечислений, но хранятся как отдельные активы. Например, посмотрите на ScriptableObject PlayerIDSO в проекте PaddleBallSO в примере ниже.
По сути, это пустой ScriptableObject.
Вы можете использовать это для создания множества активов ScriptableObject в проекте, таких как P1, P2 и т.д. Даже когда они не содержат никаких данных, вы можете использовать ScriptableObjects для сравнения. Просто создайте новый актив ScriptableObject в проекте и дайте ему имя.
Вы можете создать столько идентификаторов игроков, сколько вам нужно в проекте, и легко переключаться между ними. Просто измените назначенный актив в скрипте GameDataSO.
Если вы проверяете на равенство, это работает аналогично перечислению. Ссылаются ли две переменные на один и тот же ScriptableObject? Если да, то это один и тот же тип предмета. В противном случае это не так.
Даже без каких-либо дополнительных данных ScriptableObject представляет собой категорию или тип предмета.
ID игрока в PaddleBallSO
В PaddleBallSO PlayerIDSO становится обозначением команды. Мы используем активы P1 и P2 в GameDataSO, чтобы различать две ракетки.
Скрипт GameSetup назначает каждой ракетке идентификатор игрока. Во время игры скрипты Paddle сравнивают ввод игрока с назначенным идентификатором команды.
Это имеет применение для любого типа многопользовательской игры. В качестве альтернативы рассмотрите возможность их использования везде, где вы обращаетесь к перечислению.
Поскольку они просто назначения в инспекторе, ScriptableObjects не подвержены тем же проблемам с переименованием и переупорядочиванием.
Хотите изменить имена идентификаторов на "Игрок1" или "Игрок2" соответственно? Вы можете это сделать, и все продолжает работать. Добавление большего количества ScriptableObjects? Нет проблем – назначение актива в инспекторе остается прежним.

Демонстрация шаблонов
Это поведение полезно для создания игрового процесса. В демонстрации Шаблонов нажмите кнопку Переключить Enum, чтобы изменить команды. MonoBehaviour на DemoBall обновляет SpriteRenderer соответственно.
Наносит ли мяч урон блоку при столкновении? Узнайте, запустив быстрый тест на равенство. Вот один из способов сравнить их в примере кода ниже.
Этот метод может определить, находятся ли два GameObject в одной команде, что полезно при проверке взаимодействий между союзниками и врагами. Это простое сравнение может применяться к сбору предметов, урону или любому другому, что имеет "команду" или "выравнивание".
Расширение поведения
Веселая часть начинается, когда вы добавляете логику в свои ScriptableObjects. В отличие от обычного enum, ScriptableObject может иметь поля и методы, помимо хранения данных.
Используйте их, чтобы каждый ScriptableObject мог иметь специализированную логику сравнения. Например, вы можете иметь ScriptableObject, который определяет специальные эффекты урона (например, холод, тепло, электричество, магия и так далее).
Если ваше приложение требует системы инвентаря для экипировки игровых предметов, ScriptableObjects могут представлять типы предметов или слоты для оружия. Некоторым персонажам не разрешено держать определенные предметы? Некоторые предметы являются магическими или имеют специальные способности? Enum на основе ScriptableObject может добавлять методы для проверки этого.
MonoBehaviour DemoBall в предыдущем примере включает метод AreEqual для сравнения ScriptableObjects. При расширении поведения вы можете объединить логику сравнения внутри самого ScriptableObject.
В демонстрации Шаблонов вы можете изменить мяч, чтобы он был более избирательным при столкновении с объектом. Посмотрите на универсальный элемент для столкновения в примере кода ниже.
Это может достичь аналогичных результатов с текущей демонстрацией, но теперь у него есть поле m_Weakness. Это позволяет каждому ScriptableObject определять другой ScriptableObject для уничтожения при столкновении.
Вместо вызова метода AreEqual каждый ScriptableObject просто управляет своей собственной логикой сравнения.
Результат более гибкий и расширяемый. Вместо того чтобы заставлять мяч уничтожать блок другой команды, вы можете быть конкретными. Несколько мячей в сцене могут уничтожать разные блоки, в зависимости от их индивидуальных CollisionItems.
Это создает основу для различных, более сложных взаимодействий. Если вы хотите создать систему камень-ножницы-бумага, вы можете определить три ScriptableObjects: Камень, Бумага и Ножницы. Каждый из них может иметь свою уникальную m_Weakness и использовать метод IsWinner для обработки взаимодействий.
В отличие от перечислений, ScriptableObjects делают этот процесс модульным и адаптируемым. Нет необходимости полагаться на дополнительные структуры данных или добавлять дополнительную логику для синхронизации с отдельным набором данных. Просто добавьте дополнительное поле и/или метод для обработки логики.
Преимущества перечислений на основе ScriptableObject
Как только вы освоитесь с перечислениями на основе ScriptableObject, вы обнаружите, что они могут улучшить ваш рабочий процесс, особенно при работе с товарищами по команде. Поскольку они являются активами, их обновление создает меньше конфликтов слияния, уменьшая риск потери данных.
Добавление новых перечислений на основе ScriptableObject так же просто, как создание другого актива. В отличие от традиционных перечислений, добавление новых значений не сломает ваш существующий код. Кроме того, Unity уже имеет встроенные инструменты для поиска, фильтрации и организации их, как и любой другой актив.
В качестве бонуса использование интерфейса перетаскивания редактора позволяет вашим дизайнерам расширять игровые данные без дополнительной поддержки со стороны разработчика программного обеспечения. Вам все равно нужно будет координировать, как изначально настроить поля, но затем дизайнеры могут самостоятельно заполнить эти поля данными.

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