MVC в Unity со Scriptable Objects. Часть 3

от автора

Завершение цикла статей от Cem Ugur Karacam о реализации MVC в Unity с помощью Scriptable Objects. Прочитать предыдущие части вы можете здесь и здесь.

Это последний этап нашего проекта.

Я попытался сделать схему, чтобы проиллюстрировать рабочий процесс MVC в Unity.

В приложении Unity объекты MonoBehaviour находятся в сцене, поэтому вы можете видеть их иерархию. Но эти объекты не могут общаться друг с другом напрямую. Шаблон MVC в Unity — решение этой проблемы.

Если говорить проще: пользовательский ввод приходит в контроллер, который создает представление для модели, а представление отображает данные модели на экране.

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

Давайте продолжим проект с того, на чем мы остановились в прошлой статье. Поработаем над представлением. Я создам панель, которая будет содержать UI-объекты.

У нас есть панель, объект Image для иконки предмета и три текстовых объекта для отображения имени, типа и силы атаки. Чтобы управлять этими объектами, создадим класс в проекте с именем InfoView и добавим его в сцену.

Добавим ссылки на элементы интерфейса.

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;  public class InfoView : MonoBehaviour {     public Image icon;     public Text nameText;     public Text typeText;     public Text attackText; }

Затем создадим метод Init, чтобы настроить эти элементы согласно входным данным.

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;  public class InfoView : MonoBehaviour {     public Image icon;     public Text nameText;     public Text typeText;     public Text attackText;      public void Init(ItemData data)     {         icon.sprite = data.icon;         nameText.text = data.name;         attackText.text = "Attack Power: " + data.attack;          switch (data.type)         {             case ItemType.Axe: typeText.text    = "Type: Axe"; break;             case ItemType.Dagger: typeText.text = "Type: Dagger"; break;             case ItemType.Hammer: typeText.text = "Type: Hammer"; break;             case ItemType.Potion: typeText.text = "Type: Potion"; break;         }     } }

Теперь назначим поля в редакторе.

Мы готовы сделать префаб из этого представления. Я создал папку с именем Resources. Это особая папка, которая позволяет Unity загружать файлы из нее через специальный API. Поместим наши префабы в папку Resources.

Поскольку я буду использовать префабы, то удалю InfoView из сцены.

Время открыть контроллер.

Я добавлю открытую переменную Transform, чтобы знать, какой объект будет родительским для этого представления, и закрытую переменную, чтобы сохранить ссылку на InfoView при старте.

using System.Collections; using System.Collections.Generic; using UnityEngine;  public class ItemViewController : MonoBehaviour {     public Inventory inventoryHolder;     public Transform inventoryViewParent;     public Transform infoViewParent;      private GameObject infoViewPrefab;     private GameObject itemViewPrefab;      private void Start()      {         itemViewPrefab = (GameObject)Resources.Load("Item");         infoViewPrefab = (GameObject)Resources.Load("InfoView");     } }

Напишем метод для создания экземпляров представления в сцене.

private void CreateInfoView(ItemData data) { var infoGO = GameObject.Instantiate(infoViewPrefab, infoViewParent); infoGO.GetComponent<InfoView>().Init(data); }

Я передам этот метод ItemView в InitItem, используя события в C#. Чтобы этого добиться, изменим немного ItemView.

using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;  public class ItemView : MonoBehaviour {     public Button button;     public Image itemIcon;      private ItemData itemData;      public void InitItem(ItemData item, Action<ItemData> callback)     {         this.itemData = item;         itemIcon.sprite = itemData.icon;          button.onClick.AddListener(() => callback(itemData) );     } }

Я добавил параметр, чтобы передать метод. И теперь можно подключить контроллер.

using System.Collections; using System.Collections.Generic; using UnityEngine;  public class ItemViewController : MonoBehaviour {     public Inventory inventoryHolder;     public Transform inventoryViewParent;     public Transform infoViewParent;      private GameObject infoViewPrefab;     private GameObject itemViewPrefab;      private void Start()      {         itemViewPrefab = (GameObject)Resources.Load("Item");         infoViewPrefab = (GameObject)Resources.Load("InfoView");          foreach (var item in inventoryHolder.inventory)         {             var itemGO = GameObject.Instantiate(itemViewPrefab, inventoryViewParent);             itemGO.GetComponent<ItemView>().InitItem(item, CreateInfoView);         }     }      private void CreateInfoView(ItemData data)     {         var infoGO = GameObject.Instantiate(infoViewPrefab, infoViewParent);         infoGO.GetComponent<InfoView>().Init(data);     } }

В методе Start я заполняю инвентарь предметами, и когда вы кликнете по одному из них, будет вызван метод CreateInfoView. Но перед тем как мы начнем тестировать это, я укажу вам на одну проблему. Контроллер не знает, создавали ли мы InfoView ранее. Давайте исправим это.

using System.Collections; using System.Collections.Generic; using UnityEngine;  public class ItemViewController : MonoBehaviour {     public Inventory inventoryHolder;     public Transform inventoryViewParent;     public Transform infoViewParent;      private GameObject infoViewPrefab;     private GameObject itemViewPrefab;      private GameObject infoView;      private void Start()      {         itemViewPrefab = (GameObject)Resources.Load("Item");         infoViewPrefab = (GameObject)Resources.Load("InfoView");          foreach (var item in inventoryHolder.inventory)         {             var itemGO = GameObject.Instantiate(itemViewPrefab, inventoryViewParent);             itemGO.GetComponent<ItemView>().InitItem(item, CreateInfoView);         }     }      private void CreateInfoView(ItemData data)     {         if (infoView != null)         {             Destroy(infoView);         }          infoView = GameObject.Instantiate(infoViewPrefab, infoViewParent);         infoView.GetComponent<InfoView>().Init(data);     } } 

Мы сделали переменную infoView и проверяем ее перед созданием нового экземпляра InfoView в сцене.

Давайте протестируем.

Кажется, мы это сделали!

Проект на GitHub.

Это только основы реализации MVC в Unity с использованием Scriptable Objects. Но я верю, что подобный подход можно реализовать в любом проекте. Например, при работе с REST-вызовами этот шаблон может сэкономить вам много времени и сохранить код расширяемым. В целом в Unity достаточно сложно передать объекты сцены в код и работать с ними. Кто-то может возразить и сказать, что для этих целей можно использовать шаблон Singleton. Да, метод, который я описал, не единственный, но он весьма неплох.

Думаю, мы можем вообще не использовать шаблоны, но тогда мы ничем не будем отличаться от средневековья. 🙂

В любом случае, поскольку эта серия статей завершена, я предлагаю вам почитать другие мои тексты, в которых также рассказывается о шаблоне MVC и Scriptable Objects: Making a REST service using Node and Express to use with Unity.

На этом все. Удачного кодинга!


ссылка на оригинал статьи https://habr.com/ru/company/plarium/blog/479550/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *