Unity | 框架MVC

目录

一、MVC介绍

二、搭建UI界面

三、代码实现

1.Model层

2.View层

3.Controller层

四、MVC框架测试

五、知识补充


一、MVC介绍

  1. model:数据层。界面展示的数据(需要进行初始化、更新、保存、事件通知等操作),单例模式,不必继承MonoBehaviour。
  2. view:界面层。寻找UI,提供更新UI控件的方法,供controller调用。要挂在预制体上。
    (1)MVC是对M和V层进行了解耦,但没有完全解耦,从而诞生了MVP:对M和V层完全解耦。
    (2)MVC里view还是具备直接得到model的权限的,MVP框架是完全把视图跟模型分离了。
  3. controller:处理业务逻辑。控制View的显隐及更新,事件监听及取消。要挂在预制体上。
    (1)注意:物体隐藏时,OnDestroy不会被调用。
    (2)一个view对应一个controller。

二、搭建UI界面

搭建如下的UI界面,并制作成预制体,并放于Resources\Prefabs目录。

三、代码实现

创建三个脚本:Model.cs、View.cs、Controller.cs。并将View.cs、Controller.cs挂于预制体上。

1.Model层

cs 复制代码
using System;
using UnityEngine;

public class Model
{
    // 单例
    private static Model instance;
    public static Model Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new Model();
                instance.Init();
            }
            return instance;
        }
    }


    // 数据
    private int coinCount;
    public int CoinCount  
    {
        get { return coinCount; }
    }


    // 数据操作:初始化、更新、保存
    private void Init()
    {
        coinCount = PlayerPrefs.GetInt("CoinCount", 0);
    }
    public void UpdateCoinCount()
    {
        ++coinCount;
        Save();
    }
    private void Save()
    {
        PlayerPrefs.SetInt("CoinCount", coinCount);
        CallEvent();
    }


    // 事件:用于通知Controller进行更新View
    private event Action<Model> ModelEvent;
    public void AddEventListener(Action<Model> function)
    {
        ModelEvent += function;
    }
    public void RemoveEventListener(Action<Model> function)
    {
        ModelEvent -= function;
    }
    private void CallEvent()
    {
        ModelEvent?.Invoke(this);
    }
}

2.View层

cs 复制代码
using UnityEngine;
using UnityEngine.UI;

public class View : MonoBehaviour
{
    //提供UI控件
    public Text coinText;

    public Button addCoinButton;

    //提供更新UI控件的方法
    public void UpdateCoin(Model model)
    {
        coinText.text = model.CoinCount.ToString();
    }
}

3.Controller层

一个UI预制体对应一个View、一个Controller。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Controller : MonoBehaviour
{
    //controller控制view
    private View view;

    private static Controller controller;
    public static Controller Instance
    {
        get
        {
            return controller;
        }
    }

    //1.面板显示隐藏
    public static void Show()
    {
        if (controller == null)
        {
            var temp = Resources.Load<GameObject>("Prefabs/UIPanel");
            var go = Instantiate(temp);
            go.transform.parent = GameObject.Find("Canvas").transform;
            go.transform.localPosition = Vector3.zero;
            go.transform.localScale = Vector3.one;
            controller = go.GetComponent<Controller>();
        }
        controller.gameObject.SetActive(true);

    }

    public static void Hide()
    {
        if (controller != null)
        {
            controller.gameObject.SetActive(false);
        }
    }

    void Start()
    {
        view = GetComponent<View>();

        //2.首次更新面板数据
        view.UpdateCoin(Model.Instance);


        //3.事件监听,更新数据
        view.addCoinButton.onClick.AddListener(() =>
        {
            Model.Instance.UpdateCoinCount();
        });
        Model.Instance.AddEventListener(UpdateCoin);
    }

    private void UpdateCoin(Model model)
    {
        //2.更新面板数据
        view.UpdateCoin(model);
    }


    private void OnDestroy()
    {
        Model.Instance.RemoveEventListener(UpdateCoin);
    }
}

四、MVC框架测试

cs 复制代码
using UnityEngine;

public class TestMVC : MonoBehaviour
{
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Controller.Show();
        }
        if (Input.GetMouseButtonDown(1))
        {
            Controller.Hide();
        }
    }
}

五、知识补充

cs 复制代码
    private event Action<Model> ModelEvent;
    public void AddEventListener(Action<Model> function)
    {
        ModelEvent += function;
    }
    public void RemoveEventListener(Action<Model> function)
    {
        ModelEvent -= function;
    }
    private void CallEvent()
    {
        ModelEvent?.Invoke(this);
    }

在C#中,event关键字用于声明一个事件,它本质上是一种特殊的多播委托。`ModelEvent`在这里是一个事件,当你对其使用`+=`操作符时,你实际上是将一个回调方法添加到委托的调用列表中。如果你注册了多个回调,那么这些回调方法就会被添加到`ModelEvent`的调用链中。

当调用`ModelEvent?.Invoke(this);`时,以下是在底层发生的事情:

  1. 空值检查:`?.`是C# 6.0中引入的空条件运算符。它在尝试调用方法或访问成员之前会检查左侧的对象是否为`null`。如果`ModelEvent`不为`null`,那么调用继续;如果为`null`,那么调用就不会执行,整个表达式的结果也是`null`。
  2. Invoke方法:`Invoke`是`MulticastDelegate`类的一个方法,它负责同步地调用委托链中的每个回调方法。`this`关键字是传递给每个回调方法的参数,表示事件的发起者。
  3. 多播委托的调用链:`ModelEvent`作为一个多播委托,可以持有对多个方法的引用。当你调用`Invoke`时,它按照注册的顺序调用这些方法。如果调用链中的任何一个方法抛出异常,那么后续的方法调用将不会执行,除非你捕获并处理了这个异常。
  4. **线程安全性:**虽然`?.`操作符提供了对`null`的检查,但这并不保证线程安全性。如果在检查`ModelEvent`之后和调用`Invoke`之前,另一个线程将`ModelEvent`设置为`null`,那么仍然会抛出`NullReferenceException`。在多线程环境中,通常需要额外的同步机制来确保线程安全。
  5. **事件的触发:**如果`ModelEvent`不为空,`Invoke`会触发事件,即调用所有注册的回调方法。这些方法将按照它们被添加到委托中的顺序被调用。

在C#中,事件的使用是一种很好的设计模式,它允许对象通知其他对象发生了某些事情,而不需要知道这些对象是谁或者它们要做什么。这有助于保持代码的解耦和灵活性。

相关推荐
牙膏上的小苏打23332 小时前
Unity Surround开关后导致获取主显示器分辨率错误
unity·主屏幕
Unity大海4 小时前
诠视科技Unity SDK开发环境配置、项目设置、apk打包。
科技·unity·游戏引擎
浅陌sss10 小时前
Unity中 粒子系统使用整理(一)
unity·游戏引擎
维度攻城狮14 小时前
实现在Unity3D中仿真汽车,而且还能使用ros2控制
python·unity·docker·汽车·ros2·rviz2
为你写首诗ge17 小时前
【Unity网络编程知识】FTP学习
网络·unity
神码编程19 小时前
【Unity】 HTFramework框架(六十四)SaveDataRuntime运行时保存组件参数、预制体
unity·编辑器·游戏引擎
mqiqe20 小时前
Spring MVC 页面跳转方案与区别
python·spring·mvc
菲fay21 小时前
Unity 单例模式写法
unity·单例模式
火一线1 天前
【Framework-Client系列】UIGenerate介绍
游戏·unity
ZKY_241 天前
【工具】Json在线解析工具
unity·json