- PVSM.RU - https://www.pvsm.ru -
В этом туториале вы узнаете, как создавать и использовать Scriptable Objects в Unity. Scriptable Objects помогут усовершенствовать ваш рабочий процесс, снизить объём занимаемой памяти и даже позволят разделить архитектуру кода.
Согласно документации Unity [1], ScriptableObject — это код класса, позволяющий создавать в игре Scriptable Objects для хранения больших объёмов общих данных, не зависящих от экземпляров скриптов.
Существует множество причин для использования Scriptable Objects в Unity. Они могут снизить объём используемой под каждый дополнительный префаб памяти, потому что по своей сути Scriptable Object следуют паттерну разработки Flyweight [2].
Ещё одно преимущество Scriptable Objects, которое будет основной темой этого туториала, заключается в их использовании для удобной пересылки данных. Мы рассмотрим это свойство на примере создания лавки торговца мечами, в которой будут отображаться параметры, цены и описания различных мечей.
Примечание: в этом туториале подразумевается, что вы знакомы с редактором Unity. Вы должны разбираться в том, как править код в редакторе кода и иметь базовые знания C#. Если вам нужно повысить свои навыки Unity, изучите другие туториалы по Unity [3].
Начнём с загрузки материалов [4], которые нам потребуются.
Распакуйте скачанный файл в удобное для вас место и откройте в Unity проект Scriptable Object Tutorial-Starter.
Вы должны увидеть следующую папку, созданную как часть заготовки проекта:
Для начала перейдите в сцену Sword Merchant. Она должна выглядеть следующим образом:
Настало время для создания первого Scriptable Object!
В папке Scripts создайте новый скрипт под названием SwordData. Этот класс будет использоваться как контейнер для всех данных мечей, отображаемых в лавке торговца мечами.
Внутри этого класса начнём с наследования из ScriptableObject
вместо MonoBehaviour
:
public class SwordData : ScriptableObject
{
}
Это действие сообщает Unity, что мы по-прежнему хотим использовать возможности и методы Unity, как обычный MonoBehaviour, но нам больше не нужно прикреплять этот скрипт к GameObject. Вместо этого он будет обрабатываться как любой обычный ассет, который можно создавать аналогично созданию префаба, сцены или материала.
Заполним скрипт сериализированными полями, в которых будут содержаться все данные, соответствующие информации, отображаемой в UI 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;
}
string
, в котором будет храниться название меча.string
, в котором будет храниться описание меча.int
для хранения стоимости меча в золоте.int
для хранения урона при атаке мечом.Примечание: SerializeField
В Unity атрибут SerializeField позволяет иметь частные переменные скрипта, доступные в Инспекторе. Он позволит задавать значения в редакторе, не предоставляя доступ к переменной из других скриптов.
Каждому мечу потребуется собственная уникальная реализация Scriptable Object SwordData. Но прежде чем мы сможем создать эти реализации, нам нужно добавить Scriptable Object в Asset Menu.
Добавим наш Scriptable Object в Asset Menu, добавив к классу SwordData следующий атрибут:
[CreateAssetMenu(fileName = "New SwordData", menuName = "Sword Data", order = 51)]
public class SwordData : ScriptableObject
Если всё сделано правильно, то вы сможете зайти в Assets >> Create и увидеть в меню новый ассет Sword Data. Он должен быть расположен во второй группе под ассетом Folder:
Также можно нажать правой клавишей мыши в окне 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 Objects.
Во-первых, нам нужно добавить несколько публичных методов-получателей (getter methods), чтобы другие скрипты могли получать доступ к частным полям внутри Scriptable Object. Откроем 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.name); // 3
Debug.Log(swordData.Description); // 3
Debug.Log(swordData.Icon.name); // 3
Debug.Log(swordData.GoldCost); // 3
Debug.Log(swordData.AttackDamage); // 3
}
Вот, что мы добавили этим кодом:
Вернёмся в Unity и перейдём к окну Hierarchy. Выберите игровой объект 1_Longsword в префабе меча. Добавьте соответствующий ассет 1_Longsword Data переменной Sword Data скрипта Sword.cs в окне Inspector:
Нажмите на Play (Ctrl/Cmd + P) в редакторе Unity, а затем щёлкните самый левый меч:
В консоль должна выводиться информация, напоминающие данные, переданные из ассета Sword Data.
Scriptable Objects позволяют с лёгкостью заменять эти данные. Попробуйте вставлять разные Sword Data Scriptable Object в поле Sword Data меча.
Итак, мы создали Scriptable Object, и вы увидели, как можно получать доступ к его данным внутри игры. Но нам всё равно нужно интегрировать Sword Data с UI!
Для этого можно использовать быстрый и грязный паттерн Singleton [6]. Однако мы теперь обладаем другими возможностями…
…а именно Scriptable Objects! Воспользуемся ими для создания чистого и аккуратно разделённого на части кода.
В этом разделе вы узнаете, как создавать Game Events с помощью класса UnityEvent [7].
В папке Scripts создайте два скрипта: GameEvent.cs и GameEventListener.cs. Они зависят друг от друга, поэтому чтобы избавиться от ошибок, нужно создать оба.
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);
}
}
Вот что делает приведённый выше код:
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();
}
}
В показанном выше коде происходит дальнейшее развитие проекта:
Сложно? Ничего, со временем разберётесь!
Вернитесь в редактор Unity и создайте новую папку Game Events в Scripts >> ScriptableObjects. Затем создайте семь Game Events из Asset Menu, как мы это делали для каждого ассета Sword Data. Разместите их в новой папке Game Events.
Замените код внутри скрипта Sword.cs следующими строками:
[SerializeField]
private GameEvent OnSwordSelected; // 1
private void OnMouseDown()
{
OnSwordSelected.Raise(); // 2
}
Этот код добавляет в лавку торговца мечами две возможности:
Сохраните скрипт. Теперь в каждом GameObject меча в Hierarchy подключите соответствующее событие OnSwordSelected.
Теперь у каждого меча есть ссылка на событие, вызываемое при нажатии на меч.
Теперь нужно заставить работать UI. Наша цель заключается в отображении соответствующих данных Sword Data при нажатии на каждый меч.
Перед обновлением UI необходимо получить ссылку на каждый элемент UI. Начнём с создания нового скрипта под названием 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
}
С помощью этого кода мы добавили следующее:
Указанные выше игровые объекты расположены в SwordMerchantCanvas >> SwordMerchantPanel окна Hierarchy. Добавьте скрипт к GameObject SwordMerchantCanvas, а затем настройте все ссылки:
У всех мечей есть событие, на которое может подписаться 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. После этого выберем SwordMerchant.UpdateDisplayUI из раскрывающегося меню справа от раскрывающегося списка Runtime Only.
Будьте внимательны и используйте правильный ассет Sword Data для каждого события OnSwordSelected.
Теперь мы можем запустить игру, нажать на меч и увидеть, что UI обновился соответствующим образом!
Поскольку мы используем Game Events, то можно просто SwordMerchantCanvas, и всё по-прежнему будет работать, только без UI. Это значит, что префабы мечей отделены от SwordMerchantCanvas.
Если вы что-то упустили в процессе рассказа, то можете скачать готовый проект [4], находящийся в материалах туториала.
Если вы хотите двигаться дальше, то попытайтесь сделать так, чтобы каждый меч воспроизводил собственный звук. Попробуйте расширить Scriptable Object Sword Data и слушание событий OnSwordSelected
.
Хотите больше узнать о Unity? Посмотрите нашу серию видео [8] по Unity или прочитайте туториалы по Unity [3].
Автор: PatientZero
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/unity3d/291045
Ссылки в тексте:
[1] Unity: https://docs.unity3d.com/Manual/class-ScriptableObject.html
[2] Flyweight: http://www.gameprogrammingpatterns.com/flyweight.html
[3] туториалы по Unity: https://www.raywenderlich.com/category/unity
[4] загрузки материалов: https://koenig-media.raywenderlich.com/uploads/2018/03/ScriptableObjects.zip
[5] OnMouseDown: https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnMouseDown.html
[6] Singleton: http://gameprogrammingpatterns.com/singleton.html
[7] UnityEvent: https://docs.unity3d.com/ScriptReference/Events.UnityEvent.html
[8] серию видео: https://videos.raywenderlich.com/courses/47-beginning-c/lessons/1
[9] Источник: https://habr.com/post/421523/?utm_campaign=421523
Нажмите здесь для печати.