本文结合一个小案例,讲解MVC、MVP和MVVE在Unity中的相关应用
MVC是一种软件架构模式,全称为Model-View-Controller(模型-视图-控制器)。它将应用程序分为三个主要部分:模型(Model),视图(View)和控制器(Controller)。
模型(Model):模型表示数据模型,实现对数据的增删查改,保存加载等功能
视图(View):视图负责展示模型的数据给用户,并且给用户提供交互功能
控制器(Controller):控制器负责处理用户的输入和操作。它接收用户输入,并根据输入更新模型和视图。控制器将用户的请求转发给模型进行处理,并将结果返回给视图进行展示。
MVC的设计目标是将应用程序的逻辑分离,使得模型、视图和控制器能够独立地进行开发和维护。这种分离提高了代码的可读性、可扩展性和可维护性,同时也方便了团队合作开发。
在Unity中,MVC框架主要用在UI页面中。
这里我用一个简单的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();
}
}
创建三个脚本,分别对应Model,View,Controller,注意View和Controller需要挂载到Panel上,我使用了自建的EventCenter事件中心类来实现通知功能(可以自行实现,此处不予贴出)
///
/// Model类,负责数据的处理,加载和保存
///
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);
}
}
///
/// 视图类,负责显示UI元素,提供交互按钮
///
public class TestView : MonoBehaviour
{
public Text txtNum;
public Button btnAdd;
public Button btnMinus;
//提供面板更新的相关方法给外部
public void UpdateInfo( TestModel data)
{
txtNum.text = data.Num.ToString();
}
}
///
/// Controller类,负责处理Model和View之间的交互
///
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);
}
}
运行测试,成功实现功能
不使用MVC时,所有逻辑在一个脚本中完成,耦合度增加,可重复性差(比如我要把num的类型从int改为float,那么几乎所有方法都要进行修改,而MVC框架中只需要修改Model的部分)。
但是,使用MVC框架后,肉眼可见代码量的增加,原本40行就能实现的代码,暴增到100多行,同时结构变得更复杂,代码可读性变差。在实际开发中需要根据需求使用或不使用MVC
MVP框架是一种基于MVC模式的软件架构模式,全称为Model-View-Presenter(模型-视图-呈现器)。它在MVC模式的基础上做了一些改进,旨在进一步分离视图和模型,并引入了呈现器(主持人)(Presenter)来处理视图和模型之间的交互。
MVP框架的主要组成部分包括:
模型(Model):模型代表应用程序的数据和业务逻辑。
视图(View):视图负责展示模型的数据给用户。
呈现器(Presenter):呈现器是MVP框架的新增组件,它充当了控制器的角色。呈现器负责处理用户的输入和操作,并根据输入更新模型和视图。呈现器将用户的请求转发给模型进行处理,并将结果返回给视图进行展示。
在MVC案例中,我们在Controller中将Model传给了View进行处理,这就造成了View对Model的依赖,为了实现彻底的分离,我们需要将数据更新操作放到Controller中,这就是MVP
Model类与MVC相同,只是将修改数据的操作UpdateInfo从Model中转移到了Presenter中,可以自行测试
///
/// 视图类,负责显示UI元素,提供交互按钮
///
public class TestView_MVP : MonoBehaviour
{
public Text txtNum;
public Button btnAdd;
public Button btnMinus;
//不再提供面板更新的相关方法给外部
// public void UpdateInfo( TestModel data)
// {
// txtNum.text = data.Num.ToString();
// }
}
///
/// Presenter类,负责处理Model和View之间的交互
///
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);
}
}
MVP中的Presenter完全断绝View和Model的耦合,实现进一步的分离
MVVM是一种软件架构模式,全称为Model-View-ViewModel(模型-视图-视图模型)。它是在MVC和MVP模式的基础上发展出来的,旨在进一步分离视图和模型,并引入了视图模型(ViewModel)来处理视图和模型之间的交互。
MVVM框架的主要组成部分包括:
模型(Model):模型代表应用程序的数据和业务逻辑。
视图(View):视图负责展示模型的数据给用户。
视图模型(ViewModel):视图模型是MVVM框架的新增组件,它充当了控制器或呈现器的角色。视图模型负责将模型的数据转换为视图可以理解和展示的形式,并处理视图的用户交互。它封装了视图和模型之间的通信和数据绑定逻辑。
说人话就是UI页面和数据绑死,修改UI元素会自动修改数据,在代码中修改数据也会自动更新UI显示
MVVM主要用于窗体应用开发,与游戏开发不是很搭配,而且Unity中实现MVVM非常复杂,这里就不实现了,感兴趣的可以去搜索相关的第三方插件
MVC思想最初来自网页和软件开发,并不是为了游戏开发而生,在游戏开发中主要用在大型商业游戏中,小项目使用反而会带来更多麻烦,请酌情使用