MVC
- 一、什么是MVC
- 二、MVC小案例
- Model
- View
- Controller
- 启动脚本
- 三、小小小总结
一、什么是MVC
说起MVC,可能都知道是Model--View--Controller
,但是真要通透理解它的思想,还真是不容易。
何谓MVC呢?MVC开始是存在于桌面程序中的,M是指业务模型,V是指用户界面,C是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新。MVC百度百科 通过以上,我们大体可以知道MVC目的是将M和V分离,并且C确保M和V的同步,一旦M改变V应该同步更新。,带着这个基本思想我们看下图:
MVC目的将Model(模型)层跟View(视图)层分离,而Controller(控制层)就是将Model层跟View层联系起来。
用户在操作界面(View)时,将行为传递到控制层,控制层更新Model层,而Model层更新完成后需要通知View层完成界面更新,所以Model通知控制层需要更新View层了。View界面更新,最后反馈给用户。
这样Model跟View分离,逻辑基本都是在Controller进行操作,Model中没有任何View的数据,二者这样大大减少了代码的耦合度。
说的太多不一定能理解其好处,直接看二中的小案例
二、MVC小案例
1.使用UGUI搭建的如下图的面板,实现一个简单的UI面板。
功能是:点击主面板(MainPanel)中的面板按钮,显示玩家信息(PlayerPanel)面板,PlayerPanel面板上点击升级按钮,当前面板和主面板上对应的值改变。
Model
Model相当于数据模型,不存在任何View的数据和状态。只能被Controller访问和其它的Modelc层,通过事件的订阅
来响应Model具体的操作。
using UnityEngine;
using UnityEngine.Events;
//数据模型
public class Model
{
#region 单例模式
private static Model data = null;
public static Model Data
{
get
{
if (data == null)
{
data = new Model();
//首次创建数据初始化
data.Init();
}
return data;
}
}
#endregion 单例模式
private string playerName;
public string PlayerName
{
get { return playerName; }
}
private int playerLev;
public int PlayerLev
{
get { return playerLev; }
}
private int playerGold;
public int PlayerGold
{
get { return playerGold; }
}
private int playerHp;
public int PlayerHp
{
get { return playerHp; }
}
private int playerMp;
public int PlayerMp
{
get
{
return playerMp;
}
}
private int playerAtk;
public int PlayerAtk
{
get { return playerAtk; }
}
//数据初始化
private void Init()
{
playerName = PlayerPrefs.GetString("playerName", "11");
playerLev = PlayerPrefs.GetInt("playerLev", 1);
playerGold = PlayerPrefs.GetInt("playerGold", 50);
playerHp = PlayerPrefs.GetInt("playerHp", 100);
playerMp = PlayerPrefs.GetInt("playerMp", 200);
playerAtk = PlayerPrefs.GetInt("playerAtk", 100);
}
//升级
public void LevUp()
{
playerLev += 1;
playerGold += playerLev;
playerHp += playerLev;
playerMp += playerLev;
playerAtk += playerLev;
SaveData();
}
//保存数据
private void SaveData()
{
PlayerPrefs.SetString("playerName", "11");
PlayerPrefs.SetInt("playerLev", playerLev);
PlayerPrefs.SetInt("playerGold", playerGold);
PlayerPrefs.SetInt("playerHp", playerHp);
PlayerPrefs.SetInt("playerMp", playerMp);
PlayerPrefs.SetInt("playerAtk", playerAtk);
//更新数据
UpdateInfo();
}
//更新事件的委托
private event UnityAction<Model> UpdateEvent;
//添加监听事件
public void AddEventListener(UnityAction<Model> unityAction)
{
UpdateEvent += unityAction;
}
//移除监听事件
public void RemoveEventListener(UnityAction<Model> unityAction)
{
UpdateEvent -= unityAction;
}
//执行更新
public void UpdateInfo()
{
if (UpdateEvent != null)
{
UpdateEvent(this);
}
}
}
View
视图层用于显示UI
MainView脚本
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 主视图
/// </summary>
public class MainView : MonoBehaviour
{
public Text txtName; //姓名
public Text txtLev; //等级
public Text txtGold; //金币
public Button btnPanel;
//更新的方法
public void UpdateData(Model model)
{
txtName.text = model.PlayerName;
txtLev.text = model.PlayerLev.ToString();
txtGold.text = model.PlayerGold.ToString();
}
}
PlayerView脚本
using UnityEngine;
using UnityEngine.UI;
//视图层
public class PlayerView : MonoBehaviour
{
public Text txtLev;
public Text txtHp;
public Text txtMp;
public Text txtAtk;
public Button btnUp;
public Button btnClose;
public void UpdateData(Model model)
{
txtLev.text = model.PlayerLev.ToString();
txtHp.text = model.PlayerHp.ToString();
txtMp.text = model.PlayerMp.ToString();
txtAtk.text = model.PlayerAtk.ToString();
}
}
Controller
控制层包含了Model和View之间的联系
MainControler 脚本
using UnityEngine;
public class MainControler : MonoBehaviour
{
private MainView mainView;
private static MainControler controler;
public static MainControler Controler
{
get
{
return controler;
}
}
//显示
public static void Show()
{
if (controler == null)
{
GameObject res = Resources.Load<GameObject>("UI/MainPanel");
GameObject obj = Instantiate(res);
obj.transform.SetParent(GameObject.Find("Canvas").transform, false);
controler = obj.GetComponent<MainControler>();
}
controler.gameObject.SetActive(true);
}
//隐藏
public static void Hide()
{
if (controler != null)
{
controler.gameObject.SetActive(false);
}
}
private void Start()
{
mainView = this.GetComponent<MainView>();
//首次更新数据
mainView.UpdateData(Model.Data);
//按钮的点击事件
mainView.btnPanel.onClick.AddListener(() =>
{
print("点击了主场景中的按钮");
PlayerControler.Show();
});
//添加更新视图层中的事件
Model.Data.AddEventListener(UpdateData);
}
private void UpdateData(Model model)
{
if (mainView != null)
{
mainView.UpdateData(model);
}
}
private void OnDestroy()
{
Model.Data.RemoveEventListener(UpdateData);
}
}
PlayerControler脚本
using UnityEngine;
public class PlayerControler : MonoBehaviour
{
private PlayerView playerView;
private static PlayerControler controler = null;
public static PlayerControler Controler
{
get
{
return controler;
}
}
public static void Show()
{
if (controler == null)
{
GameObject res = Resources.Load<GameObject>("UI/PlayerPanel");
GameObject obj = Instantiate(res);
obj.transform.SetParent(GameObject.Find("Canvas").transform, false);
controler = obj.GetComponent<PlayerControler>();
}
controler.gameObject.SetActive(true);
}
public static void Hide()
{
if (controler != null)
{
controler.gameObject.SetActive(false);
}
}
private void Start()
{
playerView = this.GetComponent<PlayerView>();
playerView.UpdateData(Model.Data);
playerView.btnUp.onClick.AddListener(() =>
{
//升级按钮
Model.Data.LevUp();
});
playerView.btnClose.onClick.AddListener(() =>
{
Hide();
});
Model.Data.AddEventListener(UpdateData);
}
private void UpdateData(Model model)
{
if (playerView != null)
{
playerView.UpdateData(model);
}
}
private void OnDestroy()
{
Model.Data.RemoveEventListener(UpdateData);
}
}
脚本挂载界面
1.MainPanel界面
2.PlayerPanel界面
启动脚本
using UnityEngine;
//启动入口
public class GameMgr : MonoBehaviour
{
private void Update()
{
if (Input.GetKeyDown(KeyCode.M))
{
MainControler.Show();
}
if (Input.GetKeyDown(KeyCode.Escape))
{
MainControler.Hide();
}
}
}
运行结果:
点击PlayerPanel数据对应更新。
三、小小小总结
- 同一个模型数据可以被不同的视图反复调用,很大程度上提高了代码的重用
- Model、View、Controller三个模块相互独立,降低了代码的耦合度
当然在Unity中开发中MVC也不能适用,大多数在UI系统中才可以,比如复杂的UI逻辑中,且一般的小项目也没有必要使用。