MVC (Model-View-Controller) — это схема, предполагающая разделение данных приложения, пользовательского интерфейса и управляющей логики на три отдельных компонента, чтобы каждый из них можно было независимо модифицировать. Разработчик Cem Ugur Karacam поделился своим опытом программирования в Unity и коротко рассказал о Scriptable Objects. Мы представляем перевод его статьи, опубликованной на сайте dev.to.



Добро пожаловать в первую часть моего рассказа о реализации MVC в Unity с помощью Scriptable Objects. В этой статье я написал только про основы работы со Scriptable Objects, и в целом текст будет простым и понятным.

Для начала узнаем, что о Scriptable Objects говорят разработчики Unity:

  • Это класс, от которого можно отнаследоваться, чтобы создать объект, который не должен быть прикреплен к другому объекту в сцене.
  • Наибольшая польза от такого созданного объекта будет, если применять его только для сохранения данных.

Теперь логично спросить: почему именно Scriptable Objects?

Scriptable Objects — это часть функционала движка Unity, поэтому такие объекты имеют много встроенных возможностей для работы с редактором.

Мне нравится подход, используемый при разработке проектов на Unity. Вы можете создавать собственные инструменты и легко настраивать те, которые установлены по умолчанию. Scriptable Objects заметно расширяют ваши возможности, поскольку позволяют делать пользовательские контейнеры для данных и хранить их как ассеты в проекте.



Изображение выше — это пример использования Scriptable Object в Unity. Один файл .asset, созданный в проекте, может содержать информацию, необходимую для работы нескольких систем, и обеспечивать сохранение их настроек. Если хотите узнать больше, на YouTube есть отличное видео с презентацией про SO (Scriptable Object) под названием «Свержение тирании MonoBehaviour в выдающейся революции Scriptable Object» (Overthrowing the MonoBehaviour Tyranny in a Glorious Scriptable Object Revolution).



Еще один отличный пример показан выше. Здесь игровые объекты ссылаются на объект инвентаря (Inventory), а система сохранения (Save System) управляет ими всеми. Это изображение из другой прекрасной лекции «Архитектура игры со Scriptable Objects» (Game Architecture with Scriptable Objects), очень рекомендую с ней ознакомиться.

Другая замечательная особенность в том, что когда вы разберетесь со Scriptable Objects, то, скорее всего, не захотите больше использовать другие форматы сохранения данных, такие как JSON или XML, поскольку SO — это самый удобный формат для хранения данных в Unity.

Что ж, давайте посмотрим на Scriptable Objects в деле.

using UnityEngine;

public class ItemData : ScriptableObject
{
    public string itemName;
    public ItemType type;
    public float attack;        
}

public enum ItemType
{
    Dagger,
    Axe,
    Sword,
    Staff
}

Мы написали класс с именем ItemData и несколько свойств, типичных для RPG-игр. Далее создадим несколько таких предметов в проекте, получим к ним доступ из скрипта и выведем значения в консоль. Но сначала стоит добавить одну важную строчку в наш код.

using UnityEngine;

[CreateAssetMenu]
public class ItemData : ScriptableObject
{
    public string itemName;
    public ItemType type;
    public float attack;        
}

public enum ItemType
{
    Dagger,
    Axe,
    Sword,
    Staff
}

Атрибут CreateAssetMenu, который мы добавили перед классом ItemData, указывает Unity, что мы хотим создавать файлы .asset, в которых будет храниться наш класс, через меню. Иначе мы не сможем делать это с помощью правой кнопки мыши или кнопки Create в проектной папке.





Создадим папку с названием Items и попробуем создать объект ItemData в этой папке.



Теперь напишем другой скрипт под названием Inventory, чтобы работать с файлом данных.

using UnityEngine;

public class Inventory : MonoBehaviour
{
    public ItemData[] inventory;
}

Добавим в него метод, который будет выводить информацию в консоль. Также запустим проверку, чтобы не выходить за пределы массива, а обходить массив по кругу.

using UnityEngine;

public class Inventory : MonoBehaviour
{
    public ItemData[] inventory;

    int index = 0;

    public void NextItemInfo()
    {
        if (index > inventory.Length)
        {
            index = 0;
        }

        Debug.Log("Item name: " + inventory[index].name);
        Debug.Log ("Attack power: " + inventory[index].attack);

        switch(inventory[index].type)
        {
            case ItemType.Axe: 
            Debug.Log("Item type: Axe"); 
            break;

            case ItemType.Dagger: 
            Debug.Log("Item type: Dagger");
            break;

            case ItemType.Staff: 
            Debug.Log("Item type: Staff");
            break;

            case ItemType.Sword: 
            Debug.Log("Item type: Sword"); 
            break;
        }

        index ++;
    }

}

Я назначу вызов нашего метода на клавишу «Пробел». В Unity это сделать очень легко. Благодаря классу Input мы можем проверить в методе Update, была ли нажата требуемая клавиша.

using UnityEngine;

public class Inventory : MonoBehaviour
{
    public ItemData[] inventory;

    int index = 0;

    private void Update() 
    {
        if (Input.GetKeyDown(KeyCode.Space))    
        {
            NextItemInfo();
        }
    }

    public void NextItemInfo()
    {
        if (index > inventory.Length)
        {
            index = 0;
        }

        Debug.Log("Item name: " + inventory[index].name);
        Debug.Log ("Attack power: " + inventory[index].attack);

        switch(inventory[index].type)
        {
            case ItemType.Axe: 
            Debug.Log("Item type: Axe"); 
            break;

            case ItemType.Dagger: 
            Debug.Log("Item type: Dagger");
            break;

            case ItemType.Staff: 
            Debug.Log("Item type: Staff");
            break;

            case ItemType.Sword: 
            Debug.Log("Item type: Sword"); 
            break;
        }

        index ++;
    }

}

Пришло время переключиться обратно на редактор Unity. Мы наполним наш массив inventory ранее созданными файлами ItemData из папки Items. Но сперва создадим пустой объект в сцене и добавим на него наш скрипт Inventory.



Теперь давайте запустим проект и протестируем его.



Работает! А сейчас я хочу показать вам, что Scriptable Object может хранить не только данные, но и методы для работы с ними. Например, давайте добавим метод для вычисления цены в класс ItemData. Чтобы узнать более подробную информацию об этом, загляните в документы здесь и здесь.

using UnityEngine;

[CreateAssetMenu]
public class ItemData : ScriptableObject
{
    public string itemName;
    public ItemType type;
    public float attack;        

    public float GetPrice()
    {
        return attack * 40;
    }
}

Затем используем наш новый метод в классе Inventory.

using UnityEngine;

public class Inventory : MonoBehaviour
{
    public ItemData[] inventory;

    int index = 0;

    private void Update() 
    {
        if (Input.GetKeyDown(KeyCode.Space))    
        {
            NextItemInfo();
        }
    }

    public void NextItemInfo()
    {
        if (index == inventory.Length)
        {
            index = 0;
        }

        Debug.Log("Item name: " + inventory[index].name);
        Debug.Log ("Attack power: " + inventory[index].attack);

        switch(inventory[index].type)
        {
            case ItemType.Axe: 
            Debug.Log("Item type: Axe"); 
            break;

            case ItemType.Dagger: 
            Debug.Log("Item type: Dagger");
            break;

            case ItemType.Staff: 
            Debug.Log("Item type: Staff");
            break;

            case ItemType.Sword: 
            Debug.Log("Item type: Sword"); 
            break;
        }
        Debug.Log("Item price: " + inventory[index].GetPrice());

        index ++;
    }

}

Теперь воспроизведем все и посмотрим на результат.



Примеры из этого урока есть на GitHub!

Первая часть рассказа подошла к концу. В следующей статье мы поговорим об использовании MVC в Unity. Но, поскольку мы уже знакомы со Scriptable Objects, добавим несколько новых фишек, чтобы сделать все круче и сложнее, как поступают настоящие гуру Unity.

Комментарии (1)


  1. sith
    12.11.2019 21:24

    Ещё из больших плюсов ScriptableObject можно назвать Addressable Assets — SO c данными (и ссылками) очень легко можно хранить и изменять удалённо (Unity сама загрузит их в Runtime), при этом не нужно писать свои серверные скрипты, самому проверять версию данных и так далее — Unity всё сделает за Вас.