MVC模式是视图层、控制层、模型层代码进行分层开发,视图层只关心UI的变化,控制层获取模型对象,并在数据变化时更新视图,启到统一作用,模型层是获取和更新数据模型的,这样就不必将所有逻辑代码集中一起,看着比较混乱。
正常情况每个UI界面对应一个脚本,把逻辑全部写在一起,这样的话太过臃肿,在Unity开发时使用MVC模式去实现游戏功能模块的开发,这样的话每个脚本负责的逻辑就比较清楚了,代码也不会过于臃肿。现在我们就开始模拟一下登陆界面脚本如何使用MVC模式开发,LoginFormCtrl 的代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public sealed class LoginFormCtrl : GameComponent
{
new LoginFormView iFormView;
new LoginFormModel iFormModel;
protected override void Awake()
{
base.Awake();
iFormView = (LoginFormView)base.iFormView;
iFormModel = (LoginFormModel)base.iFormModel;
}
protected override void Start()
{
base.Start();
}
protected override void Update()
{
base.Update();
}
protected override void OnDestroy()
{
base.OnDestroy();
}
}
LoginFormView的代码如下:
public class LoginFormView: IMVController
{
public override void Ready()
{
}
public override void Renew()
{
}
public override void ShutDown()
{
}
}
LoginFormModel的代码如下:
public class LoginFormModel:IMVController
{
public override void Ready()
{
//注册消息监听
}
public override void Renew()
{
}
public override void ShutDown()
{
//取消消息监听
}
private void ReceiveLoginSuccess()
{
//TODO 登陆成功消息
}
private void ReceiveLoginFailed()
{
//TODO 登陆失败消息
}
}
IMVController的代码如下:
using UnityEngine;
public abstract class IMVController
{
protected Transform transform;
protected GameObject gameObject;
public void Init(Transform transform, GameObject gameObject)
{
this.transform = transform;
this.gameObject = gameObject;
}
public abstract void Ready();
public abstract void Renew();
public abstract void ShutDown();
}
GameComponent的代码如下:
using System;
using UnityEngine;
public abstract class GameComponent : MonoBehaviour
{
protected IMVController iFormModel, iFormView;
protected virtual void Awake()
{
string formName = this.GetType().FullName;
formName = formName.Substring(0, formName.Length - 4);
iFormView = (IMVController)Activator.CreateInstance(Type.GetType(formName + "View"));
iFormModel = (IMVController)Activator.CreateInstance(Type.GetType(formName + "Model"));
iFormView.Init(transform,gameObject);
iFormModel.Init(transform, gameObject);
}
protected virtual void Start()
{
iFormView?.Ready();
iFormModel?.Ready();
}
protected virtual void Update()
{
iFormView?.Renew();
iFormModel?.Renew();
}
protected virtual void OnDestroy()
{
iFormView?.ShutDown();
iFormModel?.ShutDown();
}
}
这里就很好诠释Unity如何去使用MVC模式去开发,LoginFormCtrl拥有LoginFormModel和LoginFormView对象,并且会把自身的transform和gameObject共享给Model和View,然后把LoginFormCtrl挂载到实际界面上即可正常使用,具体如图所示:
但是有点难受的事情是LoginFormModel、LoginFormView、LoginFormCtrl都要自己创建,导致浪费了没有必要的时间,所以这里写一个工具去自动生成这三个脚本。
这里可以在Unity上面扩展出一个界面,界面需要生成脚本名和创建脚本路径的文本框,然后点击生成按钮自动创建出MVC三个脚本,具体代码如下:
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
public class AutoMvcBuilder : EditorWindow
{
[MenuItem("Game Framework/AutoMvcBuilder Tool", false, 41)]
private static void Open()
{
Rect area = new Rect(0, 0, 300, 120);
AutoMvcBuilder window = GetWindowWithRect(area, true, "MVC脚本生成器");
window.Show();
}
static string savePath = string.Empty;
static string saveFileName = string.Empty;
static string commonContent =
"public class {0}{1}:IMVController\r\n" +
"{2}\r\n" +
" public override void Ready()\r\n" +
" {3}\r\n\r\n" +
" {4}\r\n\r\n" +
" public override void Renew()\r\n" +
" {5}\r\n\r\n" +
" {6}\r\n\r\n" +
" public override void ShutDown()\r\n" +
" {7}\r\n\r\n" +
" {8}\r\n" +
"{9}";
static string ctrlContent =
"public sealed class {0}Ctrl : GameComponent\r\n" +
"{1}\r\n" +
" new {2}View iFormView;\r\n" +
" new {3}Model iFormModel;\r\n\r\n" +
" protected override void Awake()\r\n" +
" {4}\r\n" +
" base.Awake();\r\n" +
" iFormView = ({5}View)base.iFormView;\r\n" +
" iFormModel = ({6}Model)base.iFormModel;\r\n" +
" {7}\r\n\r\n" +
" protected override void Start()\r\n" +
" {8}\r\n" +
" base.Start();\r\n" +
" {9}\r\n\r\n" +
" protected override void Update()\r\n" +
" {10}\r\n" +
" base.Update();\r\n" +
" {11}\r\n\r\n" +
" protected override void OnDestroy()\r\n" +
" {12}\r\n" +
" base.OnDestroy();\r\n" +
" {13}\r\n" +
"{14}";
private void OnGUI()
{
//输入框控件
GUIStyle buttonStyle = new GUIStyle();
buttonStyle.alignment = TextAnchor.MiddleCenter;
buttonStyle.fontSize = 15;
buttonStyle.fixedWidth = 15;
buttonStyle.normal.textColor = Color.white;
buttonStyle.normal.background = null;
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("MVC脚本名:", GUILayout.Width(80));
saveFileName = EditorGUILayout.TextField(string.Empty,saveFileName, GUILayout.Width(100));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("脚本生成路径:", GUILayout.Width(80));
savePath = EditorGUILayout.TextField(string.Empty,savePath, GUILayout.Width(190));
if (GUILayout.Button("⊙", buttonStyle))
savePath = OpenFolder();
EditorGUILayout.EndHorizontal();
GUIStyle tmpStyle = new GUIStyle();
tmpStyle.alignment = TextAnchor.MiddleCenter;
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("生成"))
{
if (!Directory.Exists(savePath))
{
this.ShowNotification(new GUIContent("路径不存在!!!"));
return;
}
File.WriteAllText(savePath + saveFileName + "Model.cs",
string.Format(commonContent,saveFileName,"Model",'{', '{', '}','{', '}','{', '}', '}'), Encoding.UTF8);
File.WriteAllText(savePath + saveFileName + "View.cs",
string.Format(commonContent, saveFileName, "View", '{', '{', '}', '{', '}', '{', '}', '}'), Encoding.UTF8);
File.WriteAllText(savePath + saveFileName + "Ctrl.cs", string.Format(ctrlContent,
saveFileName,'{', saveFileName, saveFileName,'{', saveFileName, saveFileName,'}', '{', '}', '{', '}', '{', '}', '}'), Encoding.UTF8);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
this.ShowNotification(new GUIContent("保存成功!!!"));
}
EditorGUILayout.EndHorizontal();
}
private string OpenFolder()
{
string tmpPath = string.Empty;
tmpPath = EditorUtility.OpenFolderPanel("Resource Folder", "Assets", string.Empty);
if (!string.IsNullOrEmpty(tmpPath))
{
var gamePath = System.IO.Path.GetFullPath(".");//TODO - FileUtil.GetProjectRelativePath??
gamePath = gamePath.Replace("\\", "/");
if (tmpPath.Length > gamePath.Length && tmpPath.StartsWith(gamePath))
tmpPath = tmpPath.Remove(0, gamePath.Length + 1);
}
return tmpPath + '/';
}
}
具体的运行效果图如下:
具体工程链接如下:
https://download.csdn.net/download/m0_37920739/12199626
1.MVC的优缺点
优点:1、耦合性低,视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码。2、可维护性高,便于开发和维护。
缺点:1、不适合小型,中等规模的应用程序。2、增加了系统结构和实现的复杂性
2.MVC使用场景
1.Unity、AndroidStudio这些著名开发工具的设计。
2.大型工程开发时经常用到的框架。