Для чего служит класс scriptableobject в unity
Скриптуемый объект (ScriptableObject)
ScriptableObject это класс, который позволяет вам хранить большое количество передаваемой информации независимо от образцов скрипта. Не путайте этот класс с классом под названием SerializableObject, который является классом редактора и служит для других целей. Представьте на мгновение, что вы создали префаб со скриптом, который имеет массив из миллиона целых чисел. Массив занимает 4 мегабайта памяти и принадлежит префабу. Каждый раз создавая экземпляр этого префаба, вы создаёте и экземпляр этого массива. Если вы создадите 10 игровых объектов, тогда в итоге размер занимаемый массивами для этих 10 экземпляров будет равен 40 мегабайтам.
Unity сериализует все типы примитивов, строк, массивов, списков, специфичных типов для Unity, таких как Vector3 и ваших собственных классов с атрибутом Serializable в качестве копий, относящихся к объекту, в котором они определены. Данное означает, что если вы создали класс ScriptableObject и сохранили в нём объявляемый массив из миллиона целых чисел, тогда этот массив будет передаваться вместе с этим образцом. При этом экземпляры считают, что обладают разными данными. Поля ScriptableObject или любые UnityEngine.Object поля, такие как MonoBehaviour, Mesh, GameObject и т.д, в противоположность значениям хранятся в ссылках. Если у вас есть скрипт, ссылающийся на ScriptableObject, содержащий миллион целых чисел, Unity сохранит в данных скрипта лишь ссылку на ScriptableObject. ScriptableObject в свою очередь хранит массив. 10 экземпляров префаба, которые ссылаются на класс ScriptableObject, который использует 4 мегабайта памяти, в итоге заняли бы 4 мегабайта вместо 40, о которых шла речь немного раньше.
Класс ScriptableObject необходимо использовать в тех случаях, когда нужно снизить расход памяти путём избежания копирования значений, но его также можно использовать для определения включаемых наборов данных. В качестве примера для иллюстрации его работы представьте себе NPC магазин в РПГ игре. Вы можете создать несколько ассетов вашего ShopContents (содержимого магазина) ScriptableObject, каждый из которых определял бы набор предметов, доступных для покупки. В случае, когда игра разделена на три зоны, в каждой зоне продаются свои предметы. Скрипт вашего магазина будет ссылаться на объект ShopContents, чтобы определить какие предметы доступны в данный момент. Для большего количества примеров, пожалуйста посетите руководство по скриптингу.
Once you have defined a ScriptableObject-derived class, you can use the CreateAssetMenu attribute to make it easy to create custom assets using your class.
Подсказка: при работе в инспекторе с экземплярами ScriptableObject вы можете дважды нажать на поле ссылки, чтобы открыть инспектор для своего ScriptableObject (скриптуемого объекта). Вы также можете создать пользовательский редактор для определения вида инспектора своего типа, чтобы помочь управлять данными, которые он представляет.
ScriptableObject
Every time you instantiate that Prefab, it will get its own copy of that data. Instead of using the method, and storing duplicated data, you can use a ScriptableObject to store the data and then access it by reference from all of the Prefabs. This means that there is one copy of the data in memory.
When you use the Editor, you can save data to ScriptableObjects while editing and at run time because ScriptableObjects use the Editor namespace and Editor scripting. In a deployed build, however, you can’t use ScriptableObjects to save data, but you can use the saved data from the ScriptableObject Assets that you set up during development.
Data that you save from Editor Tools to ScriptableObjects as an asset is written to disk and is therefore persistent between sessions.
This page provides an overview of the ScriptableObject class and its common uses when scripting with it. For an exhaustive reference of every member of the ScriptableObject class, see the ScriptableObject script reference.
Using a ScriptableObject
The main use cases for ScriptableObjects are:
To use a ScriptableObject, create a script in your application’s Assets Any media or data that can be used in your game or project. An asset may come from a file created outside of Unity, such as a 3D Model, an audio file or an image. You can also create some asset types in Unity, such as an Animator Controller, an Audio Mixer or a Render Texture. More info
See in Glossary folder and make it inherit from the ScriptableObject class. You can use the CreateAssetMenu attribute to make it easy to create custom assets using your class. For example:
Note: The script file must have the same name as the class.
Set the Entity To Spawn field to any Prefab in your Assets folder, then click Play in the Editor. The Prefab you referenced in the Spawner instantiates using the values you set in the SpawnManagerScriptableObject instance.
If you’re working with ScriptableObject references in the Inspector, you can double click the reference field to open the Inspector for your ScriptableObject. You can also create a custom Editor to define the look of the Inspector for your type to help manage the data that it represents.
ScriptableObject
A ScriptableObject is a data container that you can use to save large amounts of data, independent of class instances. One of the main use cases for ScriptableObjects is to reduce your Project’s memory usage by avoiding copies of values. This is useful if your Project has a Prefab that stores unchanging data in attached MonoBehaviour scripts.
Every time you instantiate that Prefab, it will get its own copy of that data. Instead of using the method, and storing duplicated data, you can use a ScriptableObject to store the data and then access it by reference from all of the Prefabs. This means that there is one copy of the data in memory.
Just like MonoBehaviours, ScriptableObjects derive from the base Unity object but, unlike MonoBehaviours, you can not attach a ScriptableObject to a GameObject. Instead, you need to save them as Assets in your Project.
When you use the Editor, you can save data to ScriptableObjects while editing and at run time because ScriptableObjects use the Editor namespace and Editor scripting. In a deployed build, however, you can’t use ScriptableObjects to save data, but you can use the saved data from the ScriptableObject Assets that you set up during development.
Data that you save from Editor Tools to ScriptableObjects as an asset is written to disk and is therefore persistent between sessions.
Using a ScriptableObject
The main use cases for ScriptableObjects are:
To use a ScriptableObject, create a script in your application’s Assets folder and make it inherit from the ScriptableObject class. You can use the CreateAssetMenu attribute to make it easy to create custom assets using your class. For example:
Attach the above script to a GameObject in your Scene. Then, in the Inspector, set the Spawn Manager Values field to the new SpawnManagerScriptableObject that you set up.
Set the Entity To Spawn field to any Prefab in your Assets folder, then click Play in the Editor. The Prefab you referenced in the Spawner instantiates using the values you set in the SpawnManagerScriptableObject instance.
If you’re working with ScriptableObject references in the Inspector, you can double click the reference field to open the Inspector for your ScriptableObject. You can also create a custom Editor to define the look of the Inspector for your type to help manage the data that it represents.
Unity: знакомство со Scriptable Objects
В этом туториале вы узнаете, как создавать и использовать Scriptable Objects в Unity. Scriptable Objects помогут усовершенствовать ваш рабочий процесс, снизить объём занимаемой памяти и даже позволят разделить архитектуру кода.
Согласно документации Unity, ScriptableObject — это код класса, позволяющий создавать в игре Scriptable Objects для хранения больших объёмов общих данных, не зависящих от экземпляров скриптов.
Существует множество причин для использования Scriptable Objects в Unity. Они могут снизить объём используемой под каждый дополнительный префаб памяти, потому что по своей сути Scriptable Object следуют паттерну разработки Flyweight.
Ещё одно преимущество Scriptable Objects, которое будет основной темой этого туториала, заключается в их использовании для удобной пересылки данных. Мы рассмотрим это свойство на примере создания лавки торговца мечами, в которой будут отображаться параметры, цены и описания различных мечей.
Примечание: в этом туториале подразумевается, что вы знакомы с редактором Unity. Вы должны разбираться в том, как править код в редакторе кода и иметь базовые знания C#. Если вам нужно повысить свои навыки Unity, изучите другие туториалы по Unity.
Приступаем к работе
Начнём с загрузки материалов, которые нам потребуются.
Распакуйте скачанный файл в удобное для вас место и откройте в Unity проект Scriptable Object Tutorial-Starter.
Вы должны увидеть следующую папку, созданную как часть заготовки проекта:
Создание Scriptable Object
Для начала перейдите в сцену Sword Merchant. Она должна выглядеть следующим образом:
Подготовка Scriptable Object
Настало время для создания первого Scriptable Object!
В папке Scripts создайте новый скрипт под названием SwordData. Этот класс будет использоваться как контейнер для всех данных мечей, отображаемых в лавке торговца мечами.
Внутри этого класса начнём с наследования из ScriptableObject вместо MonoBehaviour :
Это действие сообщает Unity, что мы по-прежнему хотим использовать возможности и методы Unity, как обычный MonoBehaviour, но нам больше не нужно прикреплять этот скрипт к GameObject. Вместо этого он будет обрабатываться как любой обычный ассет, который можно создавать аналогично созданию префаба, сцены или материала.
Заполним скрипт сериализированными полями, в которых будут содержаться все данные, соответствующие информации, отображаемой в UI Sword Merchant.
В Unity атрибут SerializeField позволяет иметь частные переменные скрипта, доступные в Инспекторе. Он позволит задавать значения в редакторе, не предоставляя доступ к переменной из других скриптов.
Каждому мечу потребуется собственная уникальная реализация Scriptable Object SwordData. Но прежде чем мы сможем создать эти реализации, нам нужно добавить Scriptable Object в Asset Menu.
Добавим наш Scriptable Object в Asset Menu, добавив к классу SwordData следующий атрибут:
Также можно нажать правой клавишей мыши в окне Project и тоже увидеть новый ассет Sword Data:
Добавление данных
Упорядочим проект, создав в папке Scripts папку с названием Scriptable Objects, а внутри этой папки — ещё одну папку с названием Sword Data.
Внутри только что созданной папки Sword Data folder создадим наш первый ассет Sword Data.
У нового ассета Sword Data по-прежнему должно быть указанное ранее имя по умолчанию fileName. Выберите ассет и дублируйте его шесть раз (Ctrl/Cmd + D), чтобы создать семь ассетов Sword Data, по одному для каждого из мечей. Теперь переименуйте каждый ассет в соответствии с префабами:
Нажмите на первый ассет Sword Data в папке Sword Data и посмотрите на окно Inspector:
Здесь мы видим ассет, в котором будет храниться информация о конкретном мече. Заполните информацию для каждого меча. Постарайтесь дать им уникальное описание, стоимость в золоте и урон при атаке. В поле Icon Sprite используйте соответствующие спрайты, расположенные в папке Sword Icons:
Поздравляю! Вы создали Scriptable Object и настроили с помощью этого Scriptable Object несколько ассетов.
Использование Scriptable Object
Теперь мы приступим к получению данных из этих Scriptable Objects.
Во-первых, нам нужно добавить несколько публичных методов-получателей (getter methods), чтобы другие скрипты могли получать доступ к частным полям внутри Scriptable Object. Откроем SwordData.cs и допишем под добавленными ранее полями следующее:
Откроем Sword.cs и добавим следующий код:
Вот, что мы добавили этим кодом:
Нажмите на Play (Ctrl/Cmd + P) в редакторе Unity, а затем щёлкните самый левый меч:
В консоль должна выводиться информация, напоминающие данные, переданные из ассета Sword Data.
Scriptable Objects позволяют с лёгкостью заменять эти данные. Попробуйте вставлять разные Sword Data Scriptable Object в поле Sword Data меча.
Scriptable Objects на основе событий
Итак, мы создали Scriptable Object, и вы увидели, как можно получать доступ к его данным внутри игры. Но нам всё равно нужно интегрировать Sword Data с UI!
Для этого можно использовать быстрый и грязный паттерн Singleton. Однако мы теперь обладаем другими возможностями…
…а именно Scriptable Objects! Воспользуемся ими для создания чистого и аккуратно разделённого на части кода.
В этом разделе вы узнаете, как создавать Game Events с помощью класса UnityEvent.
Game Events и Listeners
В папке Scripts создайте два скрипта: GameEvent.cs и GameEventListener.cs. Они зависят друг от друга, поэтому чтобы избавиться от ошибок, нужно создать оба.
Вот что делает приведённый выше код:
В показанном выше коде происходит дальнейшее развитие проекта:
Подготовка редактора
Вернитесь в редактор Unity и создайте новую папку Game Events в Scripts >> ScriptableObjects. Затем создайте семь Game Events из Asset Menu, как мы это делали для каждого ассета Sword Data. Разместите их в новой папке Game Events.
Замените код внутри скрипта Sword.cs следующими строками:
Этот код добавляет в лавку торговца мечами две возможности:
Теперь у каждого меча есть ссылка на событие, вызываемое при нажатии на меч.
Интеграция с UI
Теперь нужно заставить работать UI. Наша цель заключается в отображении соответствующих данных Sword Data при нажатии на каждый меч.
Ссылки UI
Перед обновлением UI необходимо получить ссылку на каждый элемент UI. Начнём с создания нового скрипта под названием SwordMerchant.cs и добавления в этот новый скрипт следующего кода:
С помощью этого кода мы добавили следующее:
Слушатели и отклики UI
У всех мечей есть событие, на которое может подписаться UI с помощью скрипта GameEventListener. Добавьте GameEventListener для каждого события OnSwordSelected к GameObject SwordMerchantCanvas:
Как вы могли заметить, у нашего Game Event Listener есть два поля: событие Game Event, которое он слушает, и отклик, который вызывается при генерации Game Event.
В нашем случае отклик будет обновлять UI. Добавьте в скрипт SwordMerchant.cs следующий метод:
С помощью нашего нового метода мы можем добавить отклик к каждому GameEventListener.
Для каждого добавляемого отклика необходимо в качестве значения поля None (Object) ссылку на наш игровой объект SwordMerchantCanvas. После этого выберем SwordMerchant.UpdateDisplayUI из раскрывающегося меню справа от раскрывающегося списка Runtime Only.
Будьте внимательны и используйте правильный ассет Sword Data для каждого события OnSwordSelected.
Теперь мы можем запустить игру, нажать на меч и увидеть, что UI обновился соответствующим образом!
Поскольку мы используем Game Events, то можно просто SwordMerchantCanvas, и всё по-прежнему будет работать, только без UI. Это значит, что префабы мечей отделены от SwordMerchantCanvas.
Куда двигаться дальше?
Если вы что-то упустили в процессе рассказа, то можете скачать готовый проект, находящийся в материалах туториала.
Хотите больше узнать о Unity? Посмотрите нашу серию видео по Unity или прочитайте туториалы по Unity.
Что такое ScriptableObject?
Теперь для того, чтобы можно было создавать данные непосредственно в редакторе Unity необходимо добавить атрибут CreateAssetMenu у класса объекта данных. Познакомимся с его параметрами, ближе:
Теперь мы с легкостью можем добавлять новых юнитов легким движением руки.
Структура проекта и загрузка всех данных
А я расскажу основные практики, которые использую сам при создании прототипов и своих проектов.
Resources зачем она нужна?
Папка Resources в отличии от всех других папок в структуре проекта обладает отличительным свойством. Доступ к ассетам из этой папки можно осуществить непосредственно из кода. Все данные из этой папки сохраняются при билде и могут быть использованы позже, даже если вы ни разу не использовали эти ассеты. Поэтому стоит аккуратно выбирать, какие данные вы собираетесь там хранить.
Для того, чтобы загрузить данные всех возможных юнитов непосредственно внутри игры можно воспользоваться следующим кодом:
Послесловие
Надеюсь, что мне удалось вас убедить, что ScriptableObject действительно полезный инструмент и мощный инструмент при создании игр, а если нет, то тогда ждите продолжения статьи с более широким раскрытием всех возможностей ScriptableObject.
О возможностях использования ScriptableObject можно почитать в следующих статьях:
Смотрите также:
Комментарии
лёш, ты же не против если я дополню? )
скриптаблы, для понимания могут храниться на уровне ассета и на уровне сцены.
ниже про кейс со сценой.
может создаться иллюзия, что раз скриптаблы обеспечивают ссылочность, то они могут быть хорошим решением для работы внутри ваших компонентов (тем убедительнее, что юнити сами юзают их таким образом в одном их своих видео). Но это не так и использование скриптаблов таким образом это плохая практика. По мануалам юнити рекомендует (и не без оснований) использовать скриптаблы только как ассеты.
Так же ты не упомянул несколько важных деталей про скриптейблы:
Devion, только рад за дополнение, Спасибо)
хм, а вот это интересно, не пробовал никогда такой кейс.
т.к. это UnityEngine.Object то для него обязательно учитывать операции Undo, помечать объект грязным при изменениях, и всё вытекающее.
А это я так понимаю для ситуации, когда пишется кастомный редактор для них, так?
скриптаблы не заменяют собой json, иногда нужно вынести конфиги за сам билд, тут в любом случае не будет никакого скриптабла.