该课程资源来源于唐老狮,吃水不忘打井人,不胜感激
Unity MVC框架演示 1-1 理论分析-CSDN博客
首先你需要知道什么mvc框架,并且对三个层级有个比较清晰的认识,当然不清楚也好,下面例子中将会十分细心地让你理解,你需要有一定的阅读代码的能力和一些耐心
实例场景
非常简单的小例子,不涉及任何进阶的知识
1. 有两个面板,主面板可点击角色出现的面板
2.按下M会出现主面板,按下N会隐藏,且角色面板可以通过点击与右上角的叉隐藏
3. 角色面板中,点击升级,角色属性会变化,主面板的人物等级也会变化
普通的做法
以主面板为例,拆解出如下步骤
1.获得控件
//1.获得控件
public Text txtName;
public Text txtLev;
public Text txtMoney;
public Text txtGem;
public Text txtPower;
public Button btnRole;
2.添加事件
void Start()
{
//2.添加事件
//btnRole.onClick.AddListener(ClickBtnRole);
btnRole.onClick.AddListener(() =>
{
//打开角色面板的逻辑
Debug.Log("按钮点击");
//显示角色面板
RolePanel.ShowMe();
});
}
private void ClickBtnRole()
{
//打开角色面板的逻辑
//显示角色面板
RolePanel.ShowMe();
}
3.更新信息
//3.更新信息
public void UpdateInfo()
{
//获取玩家数据 更新玩家信息
//获取玩家数据的方式 1.网络请求 2.json 3.xml 4.2进制 5.PlayerPrefs公共类
//通过PlayerPrefs来获取本地存储的玩家信息 更新到界面上
txtName.text = PlayerPrefs.GetString("PlayerName", "唐老狮");
txtLev.text = "LV." + PlayerPrefs.GetInt("PlayerLev", 1).ToString();
txtMoney.text = PlayerPrefs.GetInt("PlayerMoney", 999).ToString();
txtGem.text = PlayerPrefs.GetInt("PlayerGem", 888).ToString();
txtPower.text = PlayerPrefs.GetInt("PlayerPower", 10).ToString();
}
4.动态显隐
//4.动态显影
public static void ShowMe()
{
if(panel == null)
{
//实例化面板对象
GameObject res = Resources.Load<GameObject>("UI/MainPanel");
GameObject obj = Instantiate(res);
//设置它的父对象 为Canvas
obj.transform.SetParent(GameObject.Find("Canvas").transform, false);
panel = obj.GetComponent<MainPanel>();
}
//如果是隐藏的形式hide 在这要显示
panel.gameObject.SetActive(true);
//显示完面板 更新该面板的信息
panel.UpdateInfo();
}
发现问题
上述代码中,会发现高度耦合,这样简单的例子还好,如果项目一大起来还到处引用那就丸辣,不但乱七八糟,而且就算写了注释过几天重新阅读的难度也会比较大,又浪费时间有不优雅
所以,就出现了MVC框架,将该脚本一分为三,分别放在三个层中
分析和解决问题
那我该如何去分层呢?
在这个例子中,我们就直接将脚本分到MVC三个文件夹里,并试图让其之间产生联系为努力吧
M层---处理数据
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 作为一个唯一的数据模型 一般情况 要不自己是个单例模式对象
/// 要不自己存在在一个单例模式对象中
/// </summary>
public class PlayerModel
{
//数据内容
private string playerName;
public string PlayerName
{
get
{
return playerName;
}
}
private int lev;
public int Lev
{
get
{
return lev;
}
}
private int money;
public int Money
{
get
{
return money;
}
}
private int gem;
public int Gem
{
get
{
return gem;
}
}
private int power;
public int Power
{
get
{
return power;
}
}
private int hp;
public int HP
{
get
{
return hp;
}
}
private int atk;
public int Atk
{
get
{
return atk;
}
}
private int def;
public int Def
{
get
{
return def;
}
}
private int crit;
public int Crit
{
get
{
return crit;
}
}
private int miss;
public int Miss
{
get
{
return miss;
}
}
private int luck;
public int Luck
{
get
{
return luck;
}
}
//在外部第一次获取这个数据 如何获取
//通过单例模式 来达到数据的唯一性 和数据的获取
private static PlayerModel data = null;
public static PlayerModel Data
{
get
{
if( data == null )
{
data = new PlayerModel();
data.Init();
}
return data;
}
}
//通知外部更新的事件
//通过它和外部建立联系 而不是直接获取外部的面板
private event UnityAction<PlayerModel> updateEvent;
//数据相关的操作
// 初始化
public void Init()
{
playerName = PlayerPrefs.GetString("PlayerName", "唐老狮");
lev = PlayerPrefs.GetInt("PlayerLev", 1);
money = PlayerPrefs.GetInt("PlayerMoney", 9999);
gem = PlayerPrefs.GetInt("PlayerGem", 8888);
power = PlayerPrefs.GetInt("PlayerPower", 99);
hp = PlayerPrefs.GetInt("PlayerHp", 100);
atk = PlayerPrefs.GetInt("PlayerAtk", 20);
def = PlayerPrefs.GetInt("PlayerDef", 10);
crit = PlayerPrefs.GetInt("PlayerCrit", 20);
miss = PlayerPrefs.GetInt("PlayerMiss", 10);
luck = PlayerPrefs.GetInt("PlayerLuck", 40);
}
// 更新 升级
public void LevUp()
{
//升级 改变内容
lev += 1;
hp += lev;
atk += lev;
def += lev;
crit += lev;
miss += lev;
luck += lev;
//改变过后保存
SaveData();
}
// 保存
public void SaveData()
{
//把这些数据内容 存储到本地
PlayerPrefs.SetString("PlayerName", playerName);
PlayerPrefs.SetInt("PlayerLev", lev);
PlayerPrefs.SetInt("PlayerMoney", money);
PlayerPrefs.SetInt("PlayerGem", gem);
PlayerPrefs.SetInt("PlayerPower", power);
PlayerPrefs.SetInt("PlayerHp", hp);
PlayerPrefs.SetInt("PlayerAtk", atk);
PlayerPrefs.SetInt("PlayerDef", def);
PlayerPrefs.SetInt("PlayerCrit", crit);
PlayerPrefs.SetInt("PlayerMiss", miss);
PlayerPrefs.SetInt("PlayerLuck", luck);
//保存后执行事件更新方法
UpdateInfo();
}
public void AddEventListener(UnityAction<PlayerModel> function)
{
updateEvent += function;
}
public void RemoveEventListener(UnityAction<PlayerModel> function)
{
updateEvent -= function;
}
//通知外部更新数据的方法
private void UpdateInfo()
{
//找到对应的 使用数据的脚本 去更新数据
if( updateEvent != null )
{
updateEvent(this);
//updateEvent?.();
}
EventCenter.GetInstance().EventTrigger<PlayerModel>("玩家数据", this);
}
}
V层---负责更新来自model的数据
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MainView : MonoBehaviour
{
//1.找控件
public Button btnRole;
public Button btnSill;
public Text txtName;
public Text txtLev;
public Text txtMoney;
public Text txtGem;
public Text txtPower;
//2.提供面板更新的相关方法给外部
public void UpdateInfo( PlayerModel data )
{
txtName.text = data.PlayerName;
txtLev.text = "LV." + data.Lev;
txtMoney.text = data.Money.ToString();
txtGem.text = data.Gem.ToString();
txtPower.text = data.Power.ToString();
}
}
C层---玩家控制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Controller要处理的东西 就是业务逻辑
/// </summary>
public class MainController : MonoBehaviour
{
//能够在Controller中得到界面才行
private MainView mainView;
private static MainController controller = null;
public static MainController Controller
{
get
{
return controller;
}
}
//1.界面的显影
public static void ShowMe()
{
if (controller == null)
{
//实例化面板对象
GameObject res = Resources.Load<GameObject>("UI/MainPanel");
GameObject obj = Instantiate(res);
//设置它的父对象 为Canvas
obj.transform.SetParent(GameObject.Find("Canvas").transform, false);
controller = obj.GetComponent<MainController>();
}
controller.gameObject.SetActive(true);
}
public static void HideMe()
{
if( controller != null )
{
controller.gameObject.SetActive(false);
}
}
private void Start()
{
//获取同样挂载在一个对象上的 view脚本
mainView = this.GetComponent<MainView>();
//第一次的 界面更新
mainView.UpdateInfo(PlayerModel.Data);
//2.界面 事件的监听 来处理对应的业务逻辑
mainView.btnRole.onClick.AddListener(ClickRoleBtn);
//告知数据模块 当更新时 通知哪个函数做处理
PlayerModel.Data.AddEventListener(UpdateInfo);
}
private void ClickRoleBtn()
{
Debug.Log("点击按钮显示角色面板");
//通过Controller去显示 角色面板
RoleController.ShowMe();
}
//3.界面的更新
private void UpdateInfo( PlayerModel data )
{
if( mainView != null )
{
mainView.UpdateInfo(data);
}
}
private void OnDestroy()
{
PlayerModel.Data.RemoveEventListener(UpdateInfo);
}
}
在上述代码中,我们提取对主面板相关的内容,就有如下流程图
总结
这么看来代码量是增多了,但是确实层次结构清晰了,实际运用要看个人取舍
跨模块联系中的事件中心(也叫事件的订发布-订阅-取消模式),请参考如下文章
Unity 从零开始的框架搭建1-2 事件的发布-订阅-取消的小优化及调用对象方法总结[半干货]-CSDN博客
[干货] [非基础警告] Unity 发布-订阅模式下的事件中心设计-CSDN博客