Завершение цикла статей от Cem Ugur Karacam о реализации MVC в Unity с помощью Scriptable Objects. Прочитать предыдущие части вы можете здесь и здесь.
Это последний этап нашего проекта.
Я попытался сделать схему, чтобы проиллюстрировать рабочий процесс MVC в Unity.
В приложении Unity объекты MonoBehaviour находятся в сцене, поэтому вы можете видеть их иерархию. Но эти объекты не могут общаться друг с другом напрямую. Шаблон MVC в Unity — решение этой проблемы.
Если говорить проще: пользовательский ввод приходит в контроллер, который создает представление для модели, а представление отображает данные модели на экране.
Сначала мы ждем ввода от пользователя, например нажатия кнопки. Затем контроллер создает представление и выбирает модель, необходимую для отображения в этом представлении. Теперь представление готово, оно содержит ссылки на объекты пользовательского интерфейса и передает данные в эти ссылки для отображения.
Давайте продолжим проект с того, на чем мы остановились в прошлой статье. Поработаем над представлением. Я создам панель, которая будет содержать UI-объекты.
У нас есть панель, объект Image для иконки предмета и три текстовых объекта для отображения имени, типа и силы атаки. Чтобы управлять этими объектами, создадим класс в проекте с именем
Добавим ссылки на элементы интерфейса.
Затем создадим метод Init, чтобы настроить эти элементы согласно входным данным.
Теперь назначим поля в редакторе.
Мы готовы сделать префаб из этого представления. Я создал папку с именем
Поскольку я буду использовать префабы, то удалю
Время открыть контроллер.
Я добавлю открытую переменную
Напишем метод для создания экземпляров представления в сцене.
Я передам этот метод
Я добавил параметр, чтобы передать метод. И теперь можно подключить контроллер.
В методе Start я заполняю инвентарь предметами, и когда вы кликнете по одному из них, будет вызван метод
Мы сделали переменную
Давайте протестируем.
Кажется, мы это сделали!
Проект на GitHub.
Это только основы реализации MVC в Unity с использованием Scriptable Objects. Но я верю, что подобный подход можно реализовать в любом проекте. Например, при работе с REST-вызовами этот шаблон может сэкономить вам много времени и сохранить код расширяемым. В целом в Unity достаточно сложно передать объекты сцены в код и работать с ними. Кто-то может возразить и сказать, что для этих целей можно использовать шаблон Singleton. Да, метод, который я описал, не единственный, но он весьма неплох.
Думаю, мы можем вообще не использовать шаблоны, но тогда мы ничем не будем отличаться от средневековья. :)
В любом случае, поскольку эта серия статей завершена, я предлагаю вам почитать другие мои тексты, в которых также рассказывается о шаблоне MVC и Scriptable Objects: Making a REST service using Node and Express to use with Unity.
На этом все. Удачного кодинга!
Это последний этап нашего проекта.
Я попытался сделать схему, чтобы проиллюстрировать рабочий процесс 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.
На этом все. Удачного кодинга!