1.什么是MVC

MVC分别代表Model - View - Controller
Model - 模型层,也就是说,他只负责数据
View - 视图层,也就是说,他只负责更新UI
Controller - 控制层,他只负责控制,并关联Model和View
这样说大家可能看不懂,看一下这个图会更清晰一点
在这里插入图片描述
View 和Model基本不会直接交流,而是通过Controller进行交流

2. 准备实现的功能

在本文中以这样一个例子去讲解MVC

首先,我们搭建这样一个画面
在Canvas下创建两个Text,一个Button
在这里插入图片描述

创建后并调整的画面是这样的
在这里插入图片描述
点击按钮,Ex会减少100,生命值加100

3.设计Model层

首先是Model,最为重要的模型层,我们把上面的功能抽象成模型,只负责数据的迭代
1.HP
2.EX
3.用来升级的方法
无非就是这三个要素,首先看我的设计,在代码里会有详细的注释

//这个单参数委托,用来传递View的更新UI方法,等下就懂了哦
public delegate void BroadcastAction (PlayerModel model);

public class PlayerModel//这个也不使用Unity的API,不继承Mono
{
	//生命和经验两个变量
    private int hp = 100;
    private int ex = 10000;
    //用属性限制修改,我们不希望模型层之外的任何地方修改变量,修改只在模型层内进行
    public int HP {get { return hp; }}
    public int Ex { get { return ex; } }
    
	//这样就可以升级了,扣100经验,增加100生命
    public void AddLev()
    {
        hp+=100;
        ex-=100;
		
		//用这个方法通知View层修改数据,这个方法是接下来我们要做的,而不是原来就有的API
        Broadcast();
    }


//-------------------------分割线------------------------------------------

	//通过建立一个注册机制,来通知所有View去更新数据
	private BroadcastAction actions;
	//一旦注册也就更新
    public void Register(BroadcastAction action)
    {
        actions += action;
        Broadcast();

    }
    //通知View层更新UI,而不是直接去View层改
    public void Broadcast()
    {
        actions?.Invoke(this);
    }
//--------------------------------------分割线----------------------------
	//使用单例模式,方便外界去获取Model层,同时保证每个Model层唯一,不懂单例可以了解一下
    private static PlayerModel model;
    public static PlayerModel  Instance
    {
        get 
        {
        	//获取时如果是空,则创建一个实例
            if (model == null) model = Activator.CreateInstance<PlayerModel>();
            return model; 
        }
    }
}
}

嘿哈嘿哈,有没有发现每个Model都有后两部分内容啊,在文章末尾会简化这一过程
.

4.搭建PlayerView

在View层里,我们只负责更新UI

using UnityEngine.UI;
public class PlayerView : MonoBehaviour //需要更新UI,继承Mono
{
	//先public两个文本框,在Unity内可以通过拖拽去绑定UI
	public Text txt1,txt2;
	
	//我们只希望这里更新UI,但是我们要获取Model层的数据,也只有那里有数据
	//所以我们把Moddel作为参数传入,但传入这个参数不能是View层去做,而是Controller去传入,View层只被动接受
	//也就是说,一旦Model有变化,就通知Controller去调用View层方法去更新UI
    public void UpdateView(PlayerModel model)
    {
    	
		//更新两个文本框的内容
        txt1.text = "HP:"+model.HP.ToString();
        txt2.text = "Ex:"+model.Ex.ToString();
    }

    
}

View层就完成了,细心的小伙伴发没发现每个View都会用UpdateView这个方法呢
.

5. 搭建Controller

接下来就是关联Model和View

这Controller这一层,只负责玩家的控制MV的绑定

using UnityEngine.UI;
public class PlayerController : Controller
{
	//绑定View
    public View m_View;
	//绑定Button
    public Button btn;

    private void Start()
    {
    	//绑定MVC的过程,我们只需要把View的更新方法绑到Model的通知列表里
        PlayerModel.Instance.Register(m_View.UpdateView);
        
        //如果点击按钮执行升级,为按钮添加点击事件
        btn.onClick.AddListener(PlayerModel.Instance.AddLev);
    }
    
   
   
}

此时我们只要为这些变量添加绑定就可以了
在这里插入图片描述
有没有眼前一亮的感觉,无论计算方法怎么变,我们改的都仅仅是Model层的数据
而不关心UI的更新,或者玩家的控制
同理,我们处理UI的时候也仅仅关心UI的更新,而不考虑计算方法
实现了M - V - C 的分层,但是我们发现其中MVC都有一定的重复和规范,接下来我们着重去实现这些规范

规范的实现

1. View基类

不难发现,想要更新UI就必须继承Mono,然后想要能被Model通知更新,就需要这个共性的方法,我们只需要继承这个方法,实现这个方法,View层就成了

using UnityEngine;

public abstract class View:MonoBehaviour
{
    public abstract void UpdateView(Model model);
}

2. Controller基类

using UnityEngine;

public abstract class Controller:MonoBehaviour
{
	//这个方法我们不需要用户自己去实现,每次都一样,提供就行了,继承本类调用一次
    protected void Bind(View view,Model model)
    {
    	model.Register(view.UpdateView);
    }
}

3.Model基类

public delegate void BroadcastAction (Model model);

public abstract class Model
{
	//这是注册部分
    private BroadcastAction actions;

    public void Register(BroadcastAction action)
    {
        actions += action;
        Broadcast();
    }
    public void Broadcast()
    {
        actions?.Invoke(this);
    }
}
//多继承差不多约等于类的合并,分开是因为可以加上泛型T,直接通过类名去调单例,同时保留基类Model的兼容性
public abstract class Model<T>:Model
{
	//这是单例部分
    private static T model;
    public static T  Instance
    {
        get 
        {
            if (model == null) model = Activator.CreateInstance<T>();
            return model; 
        }
    }
}

代码简化

1.View层

这里好处不是写的少了,而是约束所有人实现View必须写这个UpdateView方法,不然就报错,如果有人View层更新写了别的方法,会引起混乱,这样有一个统一标准

using UnityEngine.UI;
public class PlayerView : View
{
    public Text txt1, txt2;
    public override void UpdateView(Model model)
    {
        PlayerModel playerModel = (PlayerModel)model;

        txt1.text = "HP:"+playerModel.HP.ToString();
        txt2.text = "Ex:"+playerModel.Money.ToString();
    }
}

2.Model层

可以看到Model层直接少了两大部分,代码量骤减,

public class PlayerModel :Model<PlayerModel>
{

    private int hp = 100;
    private int ex = 10000;
    public int HP{get { return hp; }}
    public int Ex { get { return ex; } }

    public void AddLev()
    {
        hp+=100;
        ex-=100;
        //通知View更新UI
        Broadcast();
    }
}

3.Controller

这里的作用就是一个Bind方法,让代码看起来舒服那么一点点,一点点,作用其实有限
也容易理解一点

using UnityEngine.UI;
public class PlayerController : Controller
{
    public View view;
    public Button btn;
    
	
    private void Start()
    {
        Bind(view, PlayerModel.Instance);
        btn.onClick.AddListener(PlayerModel.Instance.AddLev);
    }
}

结束

到这里,如果你认真看了这些代码,并且会使用Unity,我相信你已经理解了MVC的初级使用
哪怕是在其他语言中,其他引擎或者开发工具里,也都是一样的思路,只不过更加丰满妖娆
而不像本文只有骨架,有三分神似,我们所见只是天边一角
无限的未来等着我们去探索