Эта страница объясняет, как использовать перечисления на основе ScriptableObject в вашем проекте Unity.
Это третье из серии шести мини-руководств, созданных для того, чтобы помочь разработчикам Unity в работе с демонстрацией, которая сопровождает электронную книгу, Создание модульной игровой архитектуры в Unity с помощью ScriptableObjects.
Демонстрация вдохновлена классической механикой аркадных игр с мячом и веслом и показывает, как ScriptableObjects может помочь вам создать компоненты, которые поддаются тестированию, масштабируются и удобны для дизайнеров.
Вместе электронная книга, демонстрационный проект и эти мини-руководства предоставляют лучшие практики использования паттернов программирования с классом ScriptableObject в вашем проекте Unity. Эти советы помогут вам упростить код, уменьшить потребление памяти и способствовать повторному использованию кода.
В эту серию вошли следующие статьи:
- Начните работу с демонстрацией Unity ScriptableObjects
- Разделите игровые данные и логику с помощью ScriptableObjects
- Использование объектов ScriptableObjects в качестве объектов делегатов
- Использование ScriptableObjects в качестве каналов событий в игровом коде
- Как использовать набор времени выполнения на основе ScriptableObject
Прежде чем вы погрузитесь в демонстрационный проект ScriptableObject и эту серию мини-руководств, помните, что в своей основе паттерны проектирования - это всего лишь идеи. Они применимы не ко всем ситуациям. Эти приемы помогут вам освоить новые способы работы с Unity и ScriptableObjects.
У каждой модели есть свои плюсы и минусы. Выбирайте только те, которые приносят значительную пользу вашему конкретному проекту. Ваши дизайнеры в значительной степени полагаются на редактор Unity? Шаблон на основе ScriptableObject может стать хорошим выбором, чтобы помочь им сотрудничать с вашими разработчиками.
В конечном счете, лучшая архитектура кода - это та, которая подходит вашему проекту и команде.
Перечисления - это удобный способ управления фиксированным набором именованных значений в вашем коде. Однако они имеют некоторые ограничения. Поскольку сериализованные значения перечислений хранятся в виде целых чисел, а не их символических имен, удаление или изменение порядка значений может привести к некорректному или неожиданному поведению. Это означает, что перечисления, особенно если их много, могут создать головную боль при разработке Unity.
Стандартный подход
Вот как выглядит типичное перечисление:
[System.Serializable]
public enum HandGestures
{
Рок,
Бумага,
Ножницы
}
Вы можете сериализовать перечисление с помощью атрибута System.Serializable, и оно появится в инспекторе.
Проблема
Переупорядочивание или удаление значения может привести к проблемам. Поскольку каждое значение внутри является целым числом, то то, что оно представляет, может стать чем-то другим. В данном примере удаление значения Paper приведет к тому, что значение Scissors примет значение 1.
Или, если мы добавим значение, как в примере ниже.
Выбранное значение перечисления изменится, если оно появится после удаленной записи.
Это может вызвать проблемы при сопровождении и обновлении проектов, особенно если перечисление содержит множество значений. Вы можете смягчить эту проблему, оставив пустой или неиспользуемый элемент или явно задав целочисленные значения. Однако ни одно из этих решений не является идеальным.
Перечисления на основе ScriptableObject предоставляют вам функциональность традиционных перечислений, но хранятся в виде отдельных активов. Например, посмотрите на объект PlayerIDSO ScriptableObject в проекте PaddleBallSO в примере ниже.
По сути, это пустой объект ScriptableObject.
С его помощью вы можете создать в проекте несколько объектов ScriptableObject, например P1, P2 и т. д. Даже если они не содержат никаких данных, вы можете использовать ScriptableObjects для сравнения. Просто создайте в проекте новый актив ScriptableObject и дайте ему имя.
Вы можете создать столько идентификаторов игроков, сколько вам нужно в рамках проекта, и легко переключаться между ними. Просто измените назначенный актив в скрипте GameDataSO.
Если вы проверяете равенство, это работает аналогично перечислению. Ссылаются ли две переменные на один и тот же объект ScriptableObject? Если да, то это один и тот же тип предмета. Иначе и быть не может.
Даже без дополнительных данных объект ScriptableObject представляет категорию или тип элемента.
В PaddleBallSO PlayerIDSO становится обозначением команды. Мы используем активы P1 и P2 в GameDataSO, чтобы различать два весла.
Скрипт GameSetup присваивает каждому игроку ID. Во время игры скрипты Paddle сравнивают вводимые игроком данные с идентификатором команды.
Это применимо к любому типу многопользовательских игр. Кроме того, подумайте о том, чтобы перенять их везде, где вы обращаетесь к перечислениям.
Поскольку они являются просто назначениями в Инспекторе, ScriptableObjects не подвержены тем же проблемам с переименованием и переупорядочиванием.
Хотите изменить имена идентификаторов на "Player1" или "Player2", соответственно? Вы можете сделать это, и все продолжит работать. Добавление дополнительных объектов ScriptableObjects? Это не проблема - назначение актива в Инспекторе остается прежним.
Это поведение полезно для создания игрового процесса. В демонстрационном примере Patterns нажмите кнопку Switch Enum, чтобы сменить команду. Поведение MonoBehaviour на демонстрационном шаре соответствующим образом обновляет SpriteRenderer.
Наносит ли мяч урон блоку при столкновении? Узнайте это, проведя быстрый тест на равенство. Вот один из способов их сравнения в примере кода ниже.
Этот метод позволяет определить, находятся ли два игровых объекта в одной команде, что полезно при проверке взаимодействия дружественных и вражеских объектов. Это простое сравнение можно применить к подбору предметов, урону и всему остальному, что связано с "командой" или "выравниванием".
Самое интересное происходит, когда вы добавляете логику в объекты ScriptableObjects. В отличие от обычного перечисления, ScriptableObject может иметь поля и методы, а также содержать данные.
Используйте их, чтобы каждый объект ScriptableObject мог иметь специализированную логику сравнения. Например, у вас может быть объект ScriptableObject, определяющий специальные эффекты урона (например, холод, тепло, электричество, магия и так далее).
Если в вашем приложении требуется система инвентаря для оснащения игровых предметов, ScriptableObjects могут представлять типы предметов или слоты для оружия. Некоторым персонажам не разрешается держать определенные предметы? Являются ли некоторые предметы магическими или обладают особыми способностями? Перечисления, основанные на ScriptableObject, могут добавлять методы для проверки этого.
MonoBehaviour DemoBall в предыдущем примере включает метод AreEqual для сравнения ScriptableObjects. При расширении поведения вы можете поместить логику сравнения в сам объект ScriptableObject.
В демонстрационном примере Patterns вы можете модифицировать мяч, чтобы он был более избирательным при столкновении с объектом. Посмотрите на элемент общего назначения для столкновения в примере кода ниже.
Это может дать результаты, аналогичные текущему демо, но теперь в нем есть поле m_Weakness. Это позволяет каждому ScriptableObject определить другой ScriptableObject для уничтожения при столкновении.
Вместо того чтобы вызывать метод AreEqual, каждый объект ScriptableObject просто управляет своей собственной логикой сравнения.
Результат - более гибкие и расширяемые возможности. Вместо того чтобы уничтожать блок другой команды, вы можете сделать это конкретно. Несколько шаров в сцене могут разрушать разные блоки, в зависимости от их индивидуальных CollisionItems.
Это закладывает основу для других, более сложных взаимодействий. Если бы вы хотели создать систему "камень-ножницы-бумага", вы могли бы определить три объекта ScriptableObjects: Камень, бумага и ножницы. Каждый из них может иметь свой собственный уникальный m_Weakness и использовать метод IsWinner для обработки взаимодействий.
В отличие от перечислений, ScriptableObjects делают этот процесс модульным и адаптируемым. Нет необходимости полагаться на дополнительные структуры данных или добавлять дополнительную логику для синхронизации с отдельным набором данных. Просто добавьте дополнительное поле и/или метод для обработки логики.
Как только вы познакомитесь с перечислениями на основе ScriptableObject, вы поймете, что они могут улучшить ваш рабочий процесс, особенно при работе с партнерами по команде. Поскольку они являются активами, при их обновлении возникает меньше конфликтов слияния, что снижает риск потери данных.
Добавление новых перечислений на основе ScriptableObject - это то же самое, что создание еще одного актива. В отличие от традиционных перечислений, добавление новых значений не нарушит ваш существующий код. Кроме того, в Unity уже есть встроенные инструменты для их поиска, фильтрации и организации, как и для любых других активов.
Кроме того, использование перетаскиваемого интерфейса редактора позволяет вашим дизайнерам расширять игровые данные без дополнительной поддержки со стороны разработчика программного обеспечения. Вам все равно придется координировать начальную настройку полей, но дизайнеры смогут самостоятельно заполнять эти поля данными.
Перечисления на основе ScriptableObject - это еще один ресурс, который ваша команда может использовать для улучшения сотрудничества и повышения эффективности.
Подробнее о паттернах проектирования с помощью ScriptableObjects читайте в нашей технической электронной книге Создание модульной игровой архитектуры в Unity с помощью ScriptableObjects. Вы также можете узнать больше о распространенных паттернах разработки Unity в Повысьте уровень своего кода с помощью паттернов программирования игр.