前言

本文结合一个小案例,讲解MVC、MVP和MVVE在Unity中的相关应用

一、MVC

1、MVC是什么

MVC是一种软件架构模式,全称为Model-View-Controller(模型-视图-控制器)。它将应用程序分为三个主要部分:模型(Model),视图(View)和控制器(Controller)。

  • 模型(Model):模型表示数据模型,实现对数据的增删查改,保存加载等功能

  • 视图(View):视图负责展示模型的数据给用户,并且给用户提供交互功能

  • 控制器(Controller):控制器负责处理用户的输入和操作。它接收用户输入,并根据输入更新模型和视图。控制器将用户的请求转发给模型进行处理,并将结果返回给视图进行展示。

MVC的设计目标是将应用程序的逻辑分离,使得模型、视图和控制器能够独立地进行开发和维护。这种分离提高了代码的可读性、可扩展性和可维护性,同时也方便了团队合作开发。
在Unity中,MVC框架主要用在UI页面中。

在这里插入图片描述

2、不使用MVC的示例

这里我用一个简单的UI页面来进行展示,面板上有三个元素,一个Text负责显示数字,两个Button分别实现数字加一和减一,通过代码可以看到,我们将MVC三个部分糅合到一个脚本文件中,注释标注了每一部分对应的MVC操作
在这里插入图片描述

public class TestPanel : MonoBehaviour
{
    //View
    public Text txtNum;

    public Button btnAdd;

    public Button btnMinus;
    //Model
    private int num;
    
    
    void Start()
    {
        //Model
        num = PlayerPrefs.GetInt("Num", 100);
        //View
        btnAdd.onClick.AddListener(Add);
        btnMinus.onClick.AddListener(Minus);
    }
    //Model
    private void Add()
    {
        num = PlayerPrefs.GetInt("Num", 100);
        num++;
        PlayerPrefs.SetInt("Num",num);
        UpdateInfo();
    }
    private void Minus()
    {
        num = PlayerPrefs.GetInt("Num", 100);
        num--;
        PlayerPrefs.SetInt("Num",num);
        UpdateInfo();
    }
    //View
    private void UpdateInfo()
    {
        txtNum.text = PlayerPrefs.GetInt("Num", 100).ToString();
    }
}

3、使用MVC的示例

创建三个脚本,分别对应Model,View,Controller,注意View和Controller需要挂载到Panel上,我使用了自建的EventCenter事件中心类来实现通知功能(可以自行实现,此处不予贴出)

/// <summary>
/// Model类,负责数据的处理,加载和保存
/// </summary>
public class TestModel
{
    private int num;
    public int Num => num;
    
    private event UnityAction<TestModel> updateEvent;

    private static TestModel instance = new TestModel();

    public static TestModel Instance => instance;

    private TestModel()
    {
        Init();
    }

    private void Init()
    {
        num = PlayerPrefs.GetInt("Num", 100);
    }
    public void Add()
    {
        num++;
        //改变过后保存
        SaveData();
    }
    public void Minus()
    {
        num--;
        //改变过后保存
        SaveData();
    }
    // 保存 
    public void SaveData()
    {
        //把这些数据内容 存储到本地
        PlayerPrefs.SetInt("Num",num);
        UpdateInfo();
    }

    public void AddEventListener(UnityAction<TestModel> function)
    {
        updateEvent += function;
    }

    public void RemoveEventListener(UnityAction<TestModel> function)
    {
        updateEvent -= function;
    }

    //通知外部更新数据的方法
    private void UpdateInfo()
    {
        //找到对应的 使用数据的脚本 去更新数据
        if( updateEvent != null )
        {
            updateEvent(this);
        }

        EventCenter.GetInstance().EventTrigger<TestModel>("更新数据", this);
    }
}

/// <summary>
/// 视图类,负责显示UI元素,提供交互按钮
/// </summary>
public class TestView : MonoBehaviour
{
    public Text txtNum;
    public Button btnAdd;
    public Button btnMinus;
    //提供面板更新的相关方法给外部
    public void UpdateInfo( TestModel data)
    {
        txtNum.text = data.Num.ToString();
    }
}

/// <summary>
/// Controller类,负责处理Model和View之间的交互
/// </summary>
public class TestController : MonoBehaviour
{
    private TestView testView;

    private static TestController instance = null;
    public static TestController Instance => instance;

    private void Start()
    {
        //得到View和Controller脚本
        instance = GetComponent<TestController>();
        testView = GetComponent<TestView>();
        //第一次的界面更新
        testView.UpdateInfo(TestModel.Instance);

        //界面事件的监听 来处理对应的业务逻辑
        testView.btnAdd.onClick.AddListener(ClickAdd);
        testView.btnMinus.onClick.AddListener(ClickMinus);
        //告知数据模块 当更新时 通知哪个函数做处理
        TestModel.Instance.AddEventListener(UpdateView);
    }
    //通知Model进行num的增减
    private void ClickAdd()
    {
        TestModel.Instance.Add();
    }
    private void ClickMinus()
    {
        TestModel.Instance.Minus();
    }

    //界面的更新
    private void UpdateView( TestModel data )
    {
        if( testView != null )
        {
           testView.UpdateInfo(data);
        }
    }
    //事件监听有加就有减,防止空引用
    private void OnDestroy()
    {
        TestModel.Instance.RemoveEventListener(UpdateView);
    }
}

运行测试,成功实现功能

在这里插入图片描述

4、使用MVC和不使用的对比

不使用MVC时,所有逻辑在一个脚本中完成,耦合度增加,可重复性差(比如我要把num的类型从int改为float,那么几乎所有方法都要进行修改,而MVC框架中只需要修改Model的部分)。
但是,使用MVC框架后,肉眼可见代码量的增加,原本40行就能实现的代码,暴增到100多行,同时结构变得更复杂,代码可读性变差。在实际开发中需要根据需求使用或不使用MVC

在这里插入图片描述

在这里插入图片描述

二、MVP

1、MVP是什么(Most Valuable Player)

MVP框架是一种基于MVC模式的软件架构模式,全称为Model-View-Presenter(模型-视图-呈现器)。它在MVC模式的基础上做了一些改进,旨在进一步分离视图和模型,并引入了呈现器(主持人)(Presenter)来处理视图和模型之间的交互。

MVP框架的主要组成部分包括:

模型(Model):模型代表应用程序的数据和业务逻辑。

视图(View):视图负责展示模型的数据给用户。

呈现器(Presenter):呈现器是MVP框架的新增组件,它充当了控制器的角色。呈现器负责处理用户的输入和操作,并根据输入更新模型和视图。呈现器将用户的请求转发给模型进行处理,并将结果返回给视图进行展示。

在MVC案例中,我们在Controller中将Model传给了View进行处理,这就造成了View对Model的依赖,为了实现彻底的分离,我们需要将数据更新操作放到Controller中,这就是MVP

在这里插入图片描述

2、MVP代码展示

Model类与MVC相同,只是将修改数据的操作UpdateInfo从Model中转移到了Presenter中,可以自行测试

/// <summary>
/// 视图类,负责显示UI元素,提供交互按钮
/// </summary>
public class TestView_MVP : MonoBehaviour
{
    
        public Text txtNum;
        public Button btnAdd;
        public Button btnMinus;
        //不再提供面板更新的相关方法给外部
        // public void UpdateInfo( TestModel data)
        // {
        //     txtNum.text = data.Num.ToString();
        // }
}
/// <summary>
/// Presenter类,负责处理Model和View之间的交互
/// </summary>
public class TestPresenter : MonoBehaviour
{
    
        private TestView_MVP testView;
    
        private static TestController instance = null;
        public static TestController Instance => instance;
    
        private void Start()
        {
            //得到View和Controller脚本
            instance = GetComponent<TestController>();
            testView = GetComponent<TestView_MVP>();
            //第一次的界面更新
            //testView.UpdateInfo(TestModel.Instance);
    
            UpdateInfo(TestModel.Instance);
            
            //界面事件的监听 来处理对应的业务逻辑
            testView.btnAdd.onClick.AddListener(ClickAdd);
            testView.btnMinus.onClick.AddListener(ClickMinus);
            //告知数据模块 当更新时 通知哪个函数做处理
            TestModel.Instance.AddEventListener(UpdateInfo);
        }
        //通知Model进行num的增减
        private void ClickAdd()
        {
            TestModel.Instance.Add();
        }
        private void ClickMinus()
        {
            TestModel.Instance.Minus();
        }
    
        //界面的更新
        // private void UpdateView( TestModel data )
        // {
        //     if( testView != null )
        //     {
        //        testView.UpdateInfo(data);
        //     }
        // }
        //自己提供修改方法 而不是在Model里去更新
        private void UpdateInfo(TestModel data)
        {
            if (testView != null)
            {
                //roleView.UpdateInfo(data);
                //直接在P中得到V界面的控件 进行修改 断开V和M的联系
                testView.txtNum.text = data.Num.ToString();
            }
        }
        //事件监听有加就有减,防止空引用
        private void OnDestroy()
        {
            TestModel.Instance.RemoveEventListener(UpdateInfo);
        }
    
}

3、MVP的作用

MVP中的Presenter完全断绝View和Model的耦合,实现进一步的分离

三、MVVM

1、MVVM是什么

MVVM是一种软件架构模式,全称为Model-View-ViewModel(模型-视图-视图模型)。它是在MVC和MVP模式的基础上发展出来的,旨在进一步分离视图和模型,并引入了视图模型(ViewModel)来处理视图和模型之间的交互。

MVVM框架的主要组成部分包括:

模型(Model):模型代表应用程序的数据和业务逻辑。

视图(View):视图负责展示模型的数据给用户。

视图模型(ViewModel):视图模型是MVVM框架的新增组件,它充当了控制器或呈现器的角色。视图模型负责将模型的数据转换为视图可以理解和展示的形式,并处理视图的用户交互。它封装了视图和模型之间的通信和数据绑定逻辑。

说人话就是UI页面和数据绑死,修改UI元素会自动修改数据,在代码中修改数据也会自动更新UI显示

在这里插入图片描述

2、Unity中的MVVM

MVVM主要用于窗体应用开发,与游戏开发不是很搭配,而且Unity中实现MVVM非常复杂,这里就不实现了,感兴趣的可以去搜索相关的第三方插件

四、总结

MVC思想最初来自网页和软件开发,并不是为了游戏开发而生,在游戏开发中主要用在大型商业游戏中,小项目使用反而会带来更多麻烦,请酌情使用

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐