Contents

Unity中实现MVC

框架基类

View层

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public abstract class BaseView : MonoBehaviour
{
    /// <summary>
    /// View更新方法
    /// </summary>
    /// <param name="model"></param>
    public abstract void UpdateView<T>(T model) where T : BaseModel<T>, new();
}

Controller层

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class BaseController<T> : Single<T> where T : BaseController<T>,new()
{
    /// <summary>
    /// 绑定model和view
    /// </summary>
    /// <param name="model"></param>
    /// <param name="view"></param>
    public void Band<K>(K model, BaseView view) where K : BaseModel<K>, new()
    {
        model.AddUpdateEventListener(value => view.UpdateView(value));
    }
    protected BaseController() { }
}

Model层

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class BaseModel<T> : Single<T> where T : BaseModel<T>, new()
{
    protected BaseModel() { }

    private UnityAction<T> UpdateEvent;

    /// <summary>
    /// 更新Model数据
    /// </summary>
    public virtual void UpdateModel()
    {
        UpdateEvent?.Invoke(this as T);
    }

    /// <summary>
    /// 添加Model更新监听
    /// </summary>
    /// <param name="callback"></param>
    public void AddUpdateEventListener(UnityAction<T> callback) 
    {
        UpdateEvent -= callback;
        UpdateEvent += callback;
    }

    /// <summary>
    /// 移除Model更新监听
    /// </summary>
    /// <param name="callback"></param>
    public void RemoveUpdateEventlListener(UnityAction<T> callback) 
    {
        UpdateEvent -= callback;
    }
}

单例基类

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 单例基类(懒加载)
/// </summary>
/// <typeparam name="T"></typeparam>
public class Single<T> where T : new()
{
    private static readonly Lazy<T> instance = new(() => new T());
    public static T Instance => instance.Value;
    protected Single() { }
}

使用MVC实现对话框

DialogueModel

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class DialogueModel : BaseModel<DialogueModel>
{

    private DialogueData[] dialogueDatas;//对话数据
    private int index;//对话索引
    public bool IsLastDialogue => dialogueDatas?.Length - 1 == index;//是否最后一句话
    private readonly Dictionary<string, Sprite> avatarDict = new();//头像字典
    private string AvatarName => dialogueDatas[index].avatar == string.Empty ? dialogueDatas[index].name : dialogueDatas[index].avatar;//如果没有指定头像 使用name作为头像
    public string[] Tags => dialogueDatas[index].tag.Split('|');//字符串拆分Tag
    public string Name => dialogueDatas[index].name; //对话框 显示名字
    public Sprite Avatar => avatarDict[AvatarName];//对话框头像
    public string Value => dialogueDatas[index].value; //对话内容
    public override void UpdateModel()
    {
        //更新时加载头像 字典中有则不用加载
        if (avatarDict.ContainsKey(AvatarName))
        {
            base.UpdateModel();
        }
        else
        {
            //异步加载头像
            ResourcesManager.Instance.LoadAsync<Sprite>("Sprite/Avatar/" + AvatarName, (obj) =>
            {
                if (obj != null)
                {
                    avatarDict.Add(AvatarName, obj);
                }
                base.UpdateModel();
            });
        }
    }
    /// <summary>
    /// 加载对话
    /// </summary>
    /// <param name="dialogueID">对话id</param>
    /// <param name="callback">加载完成后调用的方法</param>
    public void LoadDialogue(string dialogueID, UnityAction callback = null)
    {
        ResourcesManager.Instance.LoadAsync<DialogueSO>("SO/" + dialogueID, (data) =>
        {
            if (data == null) return;
            dialogueDatas = data.datas;
            index = 0;
            callback?.Invoke();
            UpdateModel();
        });
    }
    /// <summary>
    /// 下一句话
    /// </summary>
    public void NextDialogue()
    {
        if (!IsLastDialogue) index++;
        UpdateModel();
    }
}

DialogueController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DialogueController : BaseController<DialogueController>
{

    private DialogueView view;

    public DialogueController()
    {
        //测试:注册切换对话Tag事件
        EventManager.Instance.AddEventListener<string>(E_EventType.DialogueTagEvent, str =>
        {
            var strs = str.Split(':');
            if (strs[0] == "Jump")
            {
                Show(strs[1]);
            }
        });
    }

    /// <summary>
    /// 显示对话
    /// </summary>
    /// <param name="id">对话id</param>
    public void Show(string id)
    {
        if (view == null)
        {
            //加载对话面板
            ResourcesManager.Instance.LoadAsync<GameObject>("UI/DialoguePanel", (obj) =>
            {
                if (obj == null) return;
                if (GameObject.Instantiate(obj, GameObject.Find("Canvas").transform, false).TryGetComponent(out view))
                {
                    Band(DialogueModel.Instance, view);
                    DialogueModel.Instance.LoadDialogue(id, () =>
                    {
                        view.gameObject.SetActive(true);
                    });
                }
            });
        }
        else
        {
            DialogueModel.Instance.LoadDialogue(id, () =>
             {
                 view.gameObject.SetActive(true);
             });
        }
    }

    /// <summary>
    /// 隐藏面板
    /// </summary>
    public void Hide()
    {
        view.gameObject.SetActive(false);
    }

    /// <summary>
    /// 下一句对话
    /// </summary>
    public void Next()
    {
        if (!view.typeWriter.IsTextShowDone) 
        {
            view.typeWriter.SkipWriteText();
            return;
        }
        //分发Tag事件
        foreach (var tagValue in DialogueModel.Instance.Tags)
        {
            EventManager.Instance.EventTrigger(E_EventType.DialogueTagEvent, tagValue);
        }
        //如果是最后一句话 隐藏面板
        if (DialogueModel.Instance.IsLastDialogue)
        {
            Hide();
            return;
        }
        //加载下一句话
        DialogueModel.Instance.NextDialogue();
    }
}

DialogueView

using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Text.RegularExpressions;

public class DialogueView : BaseView
{
    public Image imgAvatar;
    public TMP_Text textName;
    public TMP_Text textValue;
    public TypeWriter typeWriter;
    private void Start()
    {
        imgAvatar.sprite = null;
        textName.text = string.Empty;
        textValue.text = string.Empty;
    }

    /// <summary>
    /// 更新数据方法
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="model"></param>
    public override void UpdateView<T>(T model)
    {
        if (!(model is DialogueModel)) return;
        var dialogueModel = model as DialogueModel;
        imgAvatar.sprite = dialogueModel.Avatar;
        textName.text = dialogueModel.Name + ":";
        E_TypeWriterType type = E_TypeWriterType.Cover;
        float time = 0.05f;
        //测试
        foreach (var item in dialogueModel.Tags)
        {
            if (item == "Append")
            {
                type = E_TypeWriterType.Append;
            }
            else if (item.Split(':')[0] == "Time") 
            {
                time = float.Parse(item.Split(':')[1]);    
            }
        }
        typeWriter.WriteText(dialogueModel.Value, type , time);
    }
}

对话框SO

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

[CreateAssetMenu(fileName = "DialogueSO", menuName = "SO/DialogueSO", order = 0)]
public class DialogueSO : ScriptableObject
{
    public DialogueData[] datas;
}
[Serializable]
public class DialogueData
{
    [Header("角色名字")]
    public string name;
    [Header("角色头像")]
    public string avatar;
    [TextArea]
    [Header("对话内容")]
    public string value;
    [Header("标签")]
    public string tag;
}