Инструкция по ScriptableObject: Начало Работы
В этом руководстве вы узнаете, как создавать и использовать ScriptableObject в Unity.
ScriptableObjects в Unity может улучшить ваш рабочий процесс, уменьшить использование памяти и даже отделить вашу архитектуру кода.
ScriptableObject, согласно документации Unity, является наследуемым классом, который позволяет вам хранить большие объемы общих данных независимо от экземпляров скрипта.
Есть несколько причин использовать ScriptableObject в Unity. Они могут улучшить ваш рабочий процесс, уменьшить использование памяти и даже отделить вашу архитектуру кода.
Вы также уменьшите использование памяти каждым дополнительным префаб-экземпляром, поскольку по своей сути объект ScriptableObject следует шаблону проектирования Flyweight.
Еще одно преимущество ScriptableObjects, на которое будет обращено внимание в этом руководстве, заключается в их использовании для простой замены данных.
Вы будете делать это путем создания магазина Sword Merchant, который будет отображать различные характеристики, стоимость и описания для различных мечей.
Примечание: В этом руководстве предполагается, что вы знакомы с редактором Unity. Вы должны понимать, как правильно использовать редактор кода, и разбираться в языке C#.
Если вам нужно отточить свои навыки в Unity, ознакомьтесь с некоторыми другими нашими уроками по Unity.
Начнём
Для начала скачайте материалы, которые вам понадобятся для работы.
Затем распакуйте скачанные файлы в любое место по вашему выбору и откройте проект Scriptable Object Tutorial-Starter в Unity.
Вы должны увидеть следующую папку, созданную как часть начальной настройки проекта.
- Setup: Для этого урока вам вообще не нужно заходить в эту папку.
- Scenes: Тут лежит сцена Sword Merchant, на которой вы фокусируетесь в течение всего этого урока. Убедитесь, что вы сейчас в этой сцене.
- Scripts: В настоящее время тут лежит один пустой скрипт, но вы добавите больше по ходу этого урока.
- Sword Icons: Содержит эскизы каждого нужного вам меча.
- Sword Prefabs: Включает в себя сборник каждого оружия на сцене торговца мечами — Sword Merchant
Создание ScriptableObject
Во-первых, убедитесь, что вы находитесь в нужной вам сцене Sword Merchant . Должно получиться как на скриншоте выше:
Настроим Ваш ScriptableObject
Пришло время создать ваш первый ScriptableObject!
Внутри папки со скриптами Scripts создайте новый, с именем SwordData. Этот класс будет использоваться в качестве контейнера для всех данных о мечах, которые будут отображаться в вашем магазине Sword Merchant.
Внутри данного класса используйте ScriptableObject вместо MonoBehaviour:
public class SwordData : ScriptableObject
{
}
Это действие сообщает движку Unity, что вы все еще хотите использовать функции и методы Unity, например, типичный MonoBehaviour, но вам больше не нужно помещать этот скрипт в GameObject. Вместо этого он будет рассматриваться как любой другой общий ресурс, который можно создать, таким же образом как сборный объект, сцену либо материал.
Отредактируйте файл скрипта, и пропишите некоторые соответствующие поля, которые будут содержать все данные, о нужной информации, отображаемой в интерфейсе Sword Merchant.
public class SwordData : ScriptableObject
{
[SerializeField]
private string swordName;
[SerializeField]
private string description;
[SerializeField]
private Sprite icon;
[SerializeField]
private int goldCost;
[SerializeField]
private int attackDamage;
}
- swordName: Строка которая отвечает за название меча.
- description: Строка которая отвечает за его описание.
- icon: Это спрайт, или же иконка меча.
- goldCost: Параметр для обозначения стоимости меча.
- attackDamage: Отвечает за урон, или же дамаг конкретного меча.
Примечание: SerializeField
В движке Unity атрибут SerializeField даёт вам возможность, использовать частные переменные сценария, которые отображаются в Инспекторе.Это позволит вам установить параметр значения в редакторе, для чего вам даже не нужно будет предоставлять доступ к переменным из других скриптов.
Каждому мечу понадобится своя уникальная реализация SwordDataScriptableObject. Но прежде чем вы сможете создать эти реализации, вам нужно добавить свой ScriptableObject в меню ассетов.
Добавьте объект ScriptableObject в меню ассетов, присвоив классу SwordData следующий атрибут:
[CreateAssetMenu(fileName = "New SwordData", menuName = "Sword Data", order = 51)]
public class SwordData : ScriptableObject
- fileName: Имя файла по дефолту при создании ассета.
- menuName: Имя самого Asset в том виде, в котором он будет отображаться в Asset Menu.
- order: Это порядок, в каком asset будет находиться в Asset Menu. Unity группирует ассеты на подгруппы с коэффициентом 50. Таким образом, 51 поместит ваш новый asset во вторую группу Asset Menu.
Если все прошло хорошо,заходите в Assets ► Create и увидите Sword Data asset в меню. Он должен находиться во второй группе под ассетом папки:
Кроме того, нажмите ПКМ в окне проекта и там будет лежать новый ассет Sword Data:
Добавляем данные
Старайтесь быть организованным, и не ленитесь создать несколько папок. Создайте папку внутри Scripts с именем Scriptable Objects, дальше ещё одну папку в директории Scriptable Objects, дайте ей имя Sword Data.
Теперь создайте новый ассет Sword Data в папке Sword Data, которую вы только что добавили.
Новый ресурс данных меча должен по-прежнему иметь имя файла по умолчанию, указанное ранее. Выберите ресурс и продублируйте его шесть раз, пользуйтесь сочетаниями клавиш (Ctrl / Cmd + D), для создания семи наборов данных меча, по одному для каждого из мечей. Теперь дайте имя каждому ассету соответственно, чтобы соответствовать его префабу:
Жмите на первый ассет Sword Data в одноимённой папке Sword Data и взгляните на окно в инспекторе:
Здесь вы увидите ассет для хранения информации обо всех мечах. Впишите нужные данные для каждого мечика. Вам нужно будет дать им уникальное описание, цену в золоте, и не забудьте про урон от атак. Обязательно используйте нужные спрайты для каждого из них, которые находятся в папке «Sword Icons» для поля «Icon Sprite»:
Поздравляю! Вы только что создали ScriptableObject и настроили различные ресурсы с помощью этого ScriptableObject.
Используем ScriptableObject
Теперь пришло время получить данные из этих объектов, а именно из ScriptableObject. Во-первых, вам нужно добавить некоторые распространенные методы для получения доступа, чтобы другие скрипты могли получить доступ к закрытым полям внутри вашего ScriptableObject.
Откройте SwordData.cs и добавьте следующее в поля, которые вы добавили ранее:
public string SwordName
{
get
{
return swordName;
}
}
public string Description
{
get
{
return description;
}
}
public Sprite Icon
{
get
{
return icon;
}
}
public int GoldCost
{
get
{
return goldCost;
}
}
public int AttackDamage
{
get
{
return attackDamage;
}
}
Откройте скрипт Sword.cs и добавьте следующий код:
[SerializeField]
private SwordData swordData; // 1
private void OnMouseDown() // 2
{
Debug.Log(swordData.SwordName); // 3
Debug.Log(swordData.Description); // 3
Debug.Log(swordData.Icon.name); // 3
Debug.Log(swordData.GoldCost); // 3
Debug.Log(swordData.AttackDamage); // 3
}
Вот что вы добавили с помощью приведенного выше кода:
- Контейнер для данных конкретного меча.
- OnMouseDown является встроенной функцией MonoBehaviour, которая будет вызываться, при нажатии мышью.
- Примеры того, как получить данные из вашего ресурса ScriptableObject
Перейдите в Unity, дальше в окно Иерархии. Выберите 1_Longsword под игровым объектом Swords. Добавьте конкретный ассет данных 1_Longsword в слот Sword Sword Data:
Жмите играть, кнопка «Play» в редакторе Unity, а затем нажмите на меч слева.
Сейчас вы должны увидеть вывод консоли, напоминающий данные, предоставленные из ресурса Sword Data.
Объекты ScriptableObject позволяют вам легко менять эти данные. Перейдите и вставьте различные данные меча ScriptableObject в поле данных меча этого меча.
Это было довольно таки много мечей … Вот вам еще один!
Объекты основанные на событиях — Event-Based ScriptableObjects
Итак, вы создали ScriptableObject и увидели, как вы можете получить доступ к его данным в игре. Но вам все равно нужно интегрировать данные меча с пользовательским интерфейсом!
Для этого вы можете использовать шаблон, сделанный на скорую руку – Singleton. Тем не менее, у вас теперь есть новая сила..
…которой является ScriptableObjects! Используйте его в своих интересах, чтобы создать красивый, чистый и отделенный код.
В этом разделе вы научитесь создавать игровые события, с использованием класса UnityEvent.
Игровые События и слушатели, или же Listeners
В папке «Сценарии» создайте следующие два сценария GameEvent.cs и GameEventListener.cs. Они оба зависят друг от друга, поэтому вам понадобятся оба, прежде чем устранять любые ошибки.
Для начала разберемся в GameEvent:
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New Game Event", menuName = "Game Event", order = 52)] // 1
public class GameEvent : ScriptableObject // 2
{
private List<GameEventListener> listeners = new List<GameEventListener>(); // 3
public void Raise() // 4
{
for (int i = listeners.Count - 1; i >= 0; i--) // 5
{
listeners[i].OnEventRaised(); // 6
}
}
public void RegisterListener(GameEventListener listener) // 7
{
listeners.Add(listener);
}
public void UnregisterListener(GameEventListener listener) // 8
{
listeners.Remove(listener);
}
}
Вот что делает каждая строка кода, приведённого выше:
- Во первых этот код пропишет GameEvent ассетом в вашем Asset Menu.
- GameEvent — это объект ScriptableObject, поэтому он должен быть производным от объекта ScriptableObject.
- Список GameEventListeners, которые подпишутся на ваш GameEven
- Метод для вызова всех подписаных объектов GameEvent.
- Последний подписанный элемент GameEventListener будет вызван первым (последний войдёт, первый выйдет).
- Вызывает каждое событие GameEventListeners из
- Метод, который даст возможность GameEventListeners быть подписанным на GameEvent.
- Метод, который даст возможность GameEventListeners отписаться от игрового события или же GameEvent.
Теперь перейдем к GameEventListener:
using UnityEngine;
using UnityEngine.Events; // 1
public class GameEventListener : MonoBehaviour
{
[SerializeField]
private GameEvent gameEvent; // 2
[SerializeField]
private UnityEvent response; // 3
private void OnEnable() // 4
{
gameEvent.RegisterListener(this);
}
private void OnDisable() // 5
{
gameEvent.UnregisterListener(this);
}
public void OnEventRaised() // 6
{
response.Invoke();
}
}
Что же даёт нам код, указанный выше:
- Во первых он нужен чтобы использовать класс событий — UnityEvent
- Параметр GameEvent, который будет слушать GameEventListener.
- Отклик UnityEvent, который будет вызван, когда GameEvent вызовет GameEventListener.
- Вы сможете привязать GameEvent к GameEventListener, когда параметр GameObject будет включен.
- И наоборот, для того чтобы отвязать GameEvent от GameEventListener, когда параметр GameObject выключен.
- Будет вызван при генерации GameEvent, что в свою очередь приведёт к вызову Listener GameEventListener события UnityEvent.
…довольно таки трудно не правда ли?. Это большой объем информации, который вам пригодиться. Дальше будет проще.
Теперь подготовим редактор к началу работы
Идём назад в редактор Unity и создаём новую папку. С названием например Game Events, и разместите в папке Scripts >> ScriptableObjects. После этого создавайте 7 игровых событий — Game Events в меню Asset Menu, таким же образом как вы это сделали для каждого из ассетов Sword Data. Положите их в новую папку с названием Game Events.
Пропишите внутри скрипта Sword.cs следующий код:
[SerializeField]
private GameEvent OnSwordSelected; // 1
private void OnMouseDown()
{
OnSwordSelected.Raise(); // 2
}
Этот код внесёт изменения в лавку торговца мечами, а именно две новые возможности:
- Добавит генерацию Game Event когда игрок будет выбирать меч.
- Генерирует новое событие, которое будет выполнено при нажатии на меч.
Теперь можете сохранять ваш готовый скрипт. Дальше для каждого из следующих параметров GameObject меча в Hierarchy попробуйте подключить нужное вам событие OnSwordSelected.
Благодаря вам, у каждого оружия есть ссылка на event, это событие, которое произойдет при нажатии на меч.
Интеграция в UI
Теперь нужно всё настроить для корректной работы UI. Наша задача в том, чтобы при нажатии на конкретный меч, нам отображались соответствующие данные.
Ссылки интерфейса UI
Перед тем, как вы обновите UI, необходимо найти ссылку на каждый из элементов UI. Для начала зайдём в RW ► Scripts и создадим новый скрипт. Дайте ему название SwordMerchant.cs и добавьте в этот скрипт следующий код:
using UnityEngine;
using UnityEngine.UI;
public class SwordMerchant : MonoBehaviour
{
[SerializeField]
private Text swordName; // 1
[SerializeField]
private Text description; // 2
[SerializeField]
private Image icon; // 3
[SerializeField]
private Text goldCost; // 4
[SerializeField]
private Text attackDamage; // 5
}
Приписав в коде эти строки, вы только что добавили:
- Ссылку на компонент Text, который отвечает за NameText
- Ссылку на компонент с описанием объекта в игре — DescriptionText.
- Ссылку на компонент Image (картинку меча) Sword_Icon
- Ссылку на компонент Text, объекта GoldText
- Ссылку на компонент отвечающий за игровой объектAttackText.
Вышеуказанные объекты GameObject находятся в разделе SwordMerchantCanvas ► SwordMerchantPanel в окне иерархии. Добавьте новый компонент Sword Merchant в игровой объект SwordMerchantCanvas в окне иерархии, а затем установите все ссылки для него:
Listeners и Responses — Слушатели и отклики UI
У каждого из ваших мечей есть событие, на которое может подписаться интерфейс UI с использованием скрипта GameEventListener.
Теперь вам всего лишь осталось добавить GameEventListener для всех событий OnSwordSelected к GameObject SwordMerchantCanvas:
Вы наверняка заметили, что у параметра Game Event Listener существует два поля для ваших событий: а именно событие Game Event, которое он будет слушать в дальнейшем, а второе событие – является откликом, который вызывается в случае генерации самым событием Game Event. Это нам даст обновление интерфейса UI.
Пропишите следующие строки в скрипте SwordMerchant.cs:
public void UpdateDisplayUI(SwordData swordData)
{
swordName.text = swordData.SwordName;
description.text = swordData.Description;
icon.sprite = swordData.Icon;
goldCost.text = swordData.GoldCost.ToString();
attackDamage.text = swordData.AttackDamage.ToString();
}
Этот метод принимает ассет Sword Data, а затем он обновит каждое из полей UI на соответствующее значение поля Sword Data. Важно понимать, что GoldCost и AttackDamage возвращают вам int, так что для правильного отображения текста, вам нужно преобразовать его в string.
С помощью метода, который вы сегодня выучили, вы можете добавлять ответ на каждый GameEventListener. Для каждого добавляемого вами ответа, вам нужно установить в поле None (Object) ссылку на ваш SwordMerchantCanvas GameObject.
После этого выбирайте SwordMerchant.UpdateDisplayUI в выпадающем меню справа от раскрывающегося списка «Runtime Only».
Обязательно используйте правильный ассет данных меча для каждого события OnSwordSelected.
Теперь вы сможете запустить игру, и при нажатии на меч увидите что интерфейс UI обновляется корректно.
Эта установка может показаться вам немного запутанной на первый взгляд. Но так как вы используете Game Events, вы можете просто отключить SwordMerchantCanvas, и все должно работать без сбоев, просто без пользовательского интерфейса. Это значит, что префабы каждого меча отделены от SwordMerchantCanvas.
Что дальше?
Если вы пропустили что-то, вы можете скачать готовый проект.
Если вы хотите пойти еще дальше, постарайтесь, чтобы каждый меч звучал по-разному. Попробуйте раширить Sword Data ScriptableObject и слушать OnSwordSelectedGameEvents.
Хотите узнать больше о Unity? Посмотрите Unity video tutorial series или изучите другие наши уроки по Unity.
Если у вас есть вопросы или замечания, то пишите их в комментариях.