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的初级使用
哪怕是在其他语言中,其他引擎或者开发工具里,也都是一样的思路,只不过更加丰满妖娆
而不像本文只有骨架,有三分神似,我们所见只是天边一角
无限的未来等着我们去探索