有人知道做网站吗?,可以跟关键词密度过高的网站交换友情链接吗,大青海网app,网站下雪特效基本概念
MVC全名是Model View Controller 是模型(model)-视图(view)-控制器(controller)的缩写 是一种软件设计规范#xff0c;用一种业务逻辑、数据、界面显示 分离的方法组织代码 将业务逻辑聚集到一个部件里面#xff0c;在改进和个性化定制界面及用户交互的同时#x…基本概念
MVC全名是Model View Controller 是模型(model)-视图(view)-控制器(controller)的缩写 是一种软件设计规范用一种业务逻辑、数据、界面显示 分离的方法组织代码 将业务逻辑聚集到一个部件里面在改进和个性化定制界面及用户交互的同时不需要重新编写业务逻辑。 MVC在游戏开发中不是必备的它主要用于开发游戏UI系统逻辑 前期准备
接下来要实现一个小的UI面板分别实现不使用MVC框架和使用MVC框架的代码以此作为对比。
Canvas设置 非MVC框架实现
主面板逻辑
MainPanel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class MainPanel : MonoBehaviour
{//1.获得控件public Text txtName;public Text txtLev;public Text txtMoney;public Text txtGem;public Text txtPower;public Button btnRole;private static MainPanel panel;//2.添加事件//3.更新信息//4.动态显隐//使用静态方法让NormalMain能够调用public static void ShowMe(){if(panel null){//实例化面板对象GameObject res Resources.LoadGameObject(UI/MainPanel);GameObject obj Instantiate(res);//设置父对象obj.transform.SetParent(GameObject.Find(Canvas).transform,false);panel obj.GetComponentMainPanel();}//如果隐藏形式是setacive则显示也要setpanel.gameObject.SetActive(true);//显示完面板 更新panel.UpdateInfo();}public static void HideMe(){if(panel ! null){//一. 直接删// Destroy(panel.gameObject);// panel null;//二. 隐藏panel.gameObject.SetActive(false);}}// Start is called before the first frame update void Start(){//2.添加事件btnRole.onClick.AddListener(ClickBtnRole);}private void ClickBtnRole(){//打开角色面板的逻辑Debug.Log(按钮点击);}//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();}
}NormalMain.cs void Update(){if(Input.GetKeyDown(KeyCode.M)){//显示主面板MainPanel.ShowMe();}else if(Input.GetKeyDown(KeyCode.N)){//隐藏主面板MainPanel.HideMe();}}
角色面板逻辑
RolePanel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class RolePanel : MonoBehaviour
{//1.获得控件public Text txtLev;public Text txtHp;public Text txtAtk;public Text txtDef;public Text txtCrit;public Text txtMiss;public Text txtLuck;public Button btnClose;public Button btnLevUp;private static RolePanel panel;//2.添加事件//3.更新信息//4.动态显隐// Start is called before the first frame update public static void ShowMe(){if(panel null){//实例化面板对象GameObject res Resources.LoadGameObject(UI/RolePanel);GameObject obj Instantiate(res);//设置父对象obj.transform.SetParent(GameObject.Find(Canvas).transform,false);panel obj.GetComponentRolePanel();}//如果隐藏形式是setacive则显示也要setpanel.gameObject.SetActive(true);//显示完面板 更新panel.UpdateInfo();}public static void HideMe(){if(panel ! null){//一. 直接删// Destroy(panel.gameObject);// panel null;//二. 隐藏panel.gameObject.SetActive(false);}}void Start(){btnClose.onClick.AddListener((){HideMe();});btnLevUp.onClick.AddListener((){//升级就是数据更新//这里就是获取本地数据int lev PlayerPrefs.GetInt(PlayerLev,1);int hp PlayerPrefs.GetInt(PlayerHp,100);; int def PlayerPrefs.GetInt(PlayerDef,10);int atk PlayerPrefs.GetInt(PlayerAtk,20);int crit PlayerPrefs.GetInt(PlayerCrit,20);int miss PlayerPrefs.GetInt(PlayerMiss,10);int luck PlayerPrefs.GetInt(PlayerLuck,40);//然后根据升级规则去改变他lev 1;hp lev;atk lev;def lev;crit lev;miss lev;luck lev;//存起来PlayerPrefs.SetInt(PlayerLev,lev);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();//更新主面板的数据MainPanel.Panel.UpdateInfo();});}//更新信息public void UpdateInfo(){txtLev.text LV. PlayerPrefs.GetInt(PlayerLev,1).ToString();txtHp.text PlayerPrefs.GetInt(PlayerHp,100).ToString();txtAtk.text PlayerPrefs.GetInt(PlayerAtk,20).ToString();txtDef.text PlayerPrefs.GetInt(PlayerDef,10).ToString();txtCrit.text PlayerPrefs.GetInt(PlayerCrit,20).ToString();txtMiss.text PlayerPrefs.GetInt(PlayerMiss,10).ToString();txtLuck.text PlayerPrefs.GetInt(PlayerLuck,40).ToString();}
}MainPanel更新 public static MainPanel Panel{get{return panel;}} private void ClickBtnRole(){//打开角色面板的逻辑RolePanel.ShowMe();}MVC框架实现
Model数据脚本
using System.Collections;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
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 event UnityActionPlayerModel updateEvent;//在外部第一次获取这个数据 如何获取//通过单例模式 来达到数据的唯一性 和数据获取private static PlayerModel data null;public static PlayerModel Data{get{if(data null){data new PlayerModel();data.Init();}return data;}}//数据相关的操作// 初始化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);; def PlayerPrefs.GetInt(PlayerDef,10);atk PlayerPrefs.GetInt(PlayerAtk,20);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(UnityActionPlayerModel function){updateEvent function;}public void RemoveEventListener(UnityActionPlayerModel function){updateEvent - function;}//通知外面更新数据的方法private void UpdateInfo(){//找到对应的 使用数据的脚本 去更新数据if(updateEvent ! null){updateEvent(this);}}
}View界面脚本
MainView.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class MainView : MonoBehaviour
{//1.找控件public Text txtName;public Text txtLev;public Text txtMoney;public Text txtGem;public Text txtPower;public Button btnRole;public Button btnSkill;//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();}
}RoleView.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class RoleView : MonoBehaviour
{//1.找控件public Text txtLev;public Text txtHp;public Text txtAtk;public Text txtDef;public Text txtCrit;public Text txtMiss;public Text txtLuck;public Button btnClose;public Button btnLevUp;//2.提供面板更新的相关方法给外部public void UpdateInfo(PlayerModel data){txtLev.text LV. data.Lev;txtHp.text data.Hp.ToString();txtAtk.text data.Atk.ToString();txtDef.text data.Def.ToString();txtCrit.text data.Crit.ToString();txtMiss.text data.Miss.ToString();txtLuck.text data.Luck.ToString();}
}Controller业务逻辑
MainController
using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEngine;/// summary
/// Controller要处理的东西 就是业务逻辑
/// /summary
public class MainController : MonoBehaviour
{//能够在Controller中得到界面才行private MainView mainView;//面板之间的交互都是通过Controller来实现我们不想让mainView也变成静态被其它访问//因此可以设置个静态的 Controller因为Controller也要被外部访问private static MainController controller null;public static MainController Controller{get{return controller;}}//1.界面的显隐public static void ShowMe(){if(controller null){//实例化面板对象GameObject res Resources.LoadGameObject(UI/MainPanel);GameObject obj Instantiate(res);//设置父对象obj.transform.SetParent(GameObject.Find(Canvas).transform,false);controller obj.GetComponentMainController();}//如果隐藏形式是setacive则显示也要setcontroller.gameObject.SetActive(true);}public static void HideMe(){if(controller ! null){controller.gameObject.SetActive(false);}}private void Start(){//获取同样挂载在一个对象上的 view脚本mainView this.GetComponentMainView();//第一次更新mainView.UpdateInfo(PlayerModel.Data);//2.界面 事件的监听 来处理对应的业务逻辑mainView.btnRole.onClick.AddListener(ClickRoleBtn);//PlayerModel.Data.AddEventListener(mainView.UpdateInfo);PlayerModel.Data.AddEventListener(UpdateInfo);}private void ClickRoleBtn(){RoleController.ShowMe();}//3. 界面的更新private void UpdateInfo(PlayerModel data){if(mainView ! null){mainView.UpdateInfo(data);} }private void OnDestroy() {PlayerModel.Data.RemoveEventListener(UpdateInfo);}
}
RoleController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Timeline;public class RoleController : MonoBehaviour
{private RoleView roleView;private static RoleController controller null;public static RoleController Controller{get{return controller;}}public static void ShowMe(){if(controller null){//实例化面板对象GameObject res Resources.LoadGameObject(UI/RolePanel);GameObject obj Instantiate(res);//设置父对象obj.transform.SetParent(GameObject.Find(Canvas).transform,false);controller obj.GetComponentRoleController();}//如果隐藏形式是setacive则显示也要setcontroller.gameObject.SetActive(true);}public static void HideMe(){if(controller ! null){controller.gameObject.SetActive(false);}}void Start(){roleView this.GetComponentRoleView();//第一次更新面板roleView.UpdateInfo(PlayerModel.Data);roleView.btnClose.onClick.AddListener(ClickCloseBtn);roleView.btnLevUp.onClick.AddListener(ClickLevUpBtn);//PlayerModel.Data.AddEventListener(roleView.UpdateInfo);PlayerModel.Data.AddEventListener(UpdateInfo);}private void ClickCloseBtn(){HideMe();}private void ClickLevUpBtn(){//通过数据模块 进行升级 达到数据改变PlayerModel.Data.LevUp();}// Update is called once per frameprivate void UpdateInfo(PlayerModel data){if(roleView ! null){roleView.UpdateInfo(data);} }private void OnDestroy() {PlayerModel.Data.RemoveEventListener(UpdateInfo);}
}对比与总结 好处
1.各司其职互不干涉 --编程思路更清晰 2.有利开发中的分工 -- 多人协同开发时同步并行 3.有利于组件重用 -- 项目换皮时功能变化小时提高开发效率
坏处
1.增加了程序文件的体量 -- 脚本由一变三 2.增加了结构的复杂性 --对于不清楚MVC原理的人不友好 3.效率相对较低 -- 对象之间的相互跳转始终伴随着一定开销
扩展
MVC的美中不足
M和V之间存在着联系也就是数据和界面之间存在着耦合性当数据结构改变时会牵扯界面逻辑随之改动。 在MVC中当需求变化时需要维护的对象数量会增加
如这一段 改变了PlayerModel中的变量的话则MainView里的这个函数可能也要调整。
MVX
数据和界面是必备的内容 我们可以通过改变X元素来优化原本的MVC 也就是改变联系和处理M(数据)和V(界面)的方式
MVP:切断View和Model的耦合让Presenter处理一切MVVM:MVP的升级版让ViewModel和V进行双向数据绑定更新VM等同于更新V反之同理MVE:用EventCenter事件中心来分发消息
学习MVX的目的 不要拘泥于框架结构和设计模式要找到一个适合自己项目的 一个稳定的有序的能满足项目需求的实现方式 MVP
全称为模型(Model)-视图(View)一主持人(Presenter)
Model提供数据View负责界面Presenter负责逻辑的处理
它是MVC的一种变式是针对MVC中M和V存在耦合的优化
与MVC的区别
在MVC中View会直接从Model中读取数据而不是通过 Controller。 而在MVP中View并不直接使用Model它们之间的通信是通过Presenter来进行的所有的交互都发生在Presenter内部。 同样是上面的项目
MVP_MainView.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class MVP_MainView : MonoBehaviour
{//1.找控件public Text txtName;public Text txtLev;public Text txtMoney;public Text txtGem;public Text txtPower;public Button btnRole;public Button btnSkill;// //2.提供面板更新的方法给外部// public void UpdateInfo(string name, int lev, int money, int gem, int power){// txtName.text name;// txtLev.text LV. lev;// txtMoney.text money.ToString();// txtGem.text gem.ToString();// txtPower.text power.ToString();// }
}MVP_RoleView.cs
public class MVP_RoleView : MonoBehaviour
{//1.找控件public Text txtLev;public Text txtHp;public Text txtAtk;public Text txtDef;public Text txtCrit;public Text txtMiss;public Text txtLuck;public Button btnClose;public Button btnLevUp;//2.提供面板更新的相关方法给外部//方法可选 到时候可以直接在P里面通过访问控件 去修改}
MainPresenter.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class MainPresenter : MonoBehaviour
{//能够在Presenter中得到界面才行private MVP_MainView mainView;//面板之间的交互都是通过Controller来实现我们不想让mainView也变成静态被其它访问//因此可以设置个静态的 Controller因为Controller也要被外部访问private static MainPresenter presenter null;public static MainPresenter Presenter{get{return presenter;}}//1.界面的显隐public static void ShowMe(){if(presenter null){//实例化面板对象GameObject res Resources.LoadGameObject(UI/MainPanel);GameObject obj Instantiate(res);//设置父对象obj.transform.SetParent(GameObject.Find(Canvas).transform,false);presenter obj.GetComponentMainPresenter();}//如果隐藏形式是setacive则显示也要setpresenter.gameObject.SetActive(true);}public static void HideMe(){if(presenter ! null){presenter.gameObject.SetActive(false);}}private void Start(){//获取同样挂载在一个对象上的 view脚本mainView this.GetComponentMVP_MainView();//第一次更新//mainView.UpdateInfo(PlayerModel.Data);//通过P自己的更新方法来更新UpdateInfo(PlayerModel.Data);//2.界面 事件的监听 来处理对应的业务逻辑mainView.btnRole.onClick.AddListener(ClickRoleBtn);//PlayerModel.Data.AddEventListener(mainView.UpdateInfo);PlayerModel.Data.AddEventListener(UpdateInfo);}private void ClickRoleBtn(){//RoleController.ShowMe();RolePresenter.ShowMe();}//3. 界面的更新private void UpdateInfo(PlayerModel data){if(mainView ! null){//mainView.UpdateInfo(data);//以前是把数据M传到V中去更新现在全部由P来做mainView.txtName.text data.PlayerName;mainView.txtLev.text LV. data.Lev;mainView.txtMoney.text data.Money.ToString();mainView.txtGem.text data.Gem.ToString();mainView.txtPower.text data.Power.ToString();} }private void OnDestroy() {PlayerModel.Data.RemoveEventListener(UpdateInfo);}
}RolePresenter.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class RolePresenter : MonoBehaviour
{private MVP_RoleView roleView;private static RolePresenter presenter null;public static RolePresenter Presenter{get{return presenter;}}public static void ShowMe(){if(presenter null){//实例化面板对象GameObject res Resources.LoadGameObject(UI/RolePanel);GameObject obj Instantiate(res);//设置父对象obj.transform.SetParent(GameObject.Find(Canvas).transform,false);presenter obj.GetComponentRolePresenter ();}//如果隐藏形式是setacive则显示也要setpresenter.gameObject.SetActive(true);}public static void HideMe(){if(presenter ! null){presenter.gameObject.SetActive(false);}}void Start(){roleView this.GetComponentMVP_RoleView();//第一次更新面板UpdateInfo(PlayerModel.Data);roleView.btnClose.onClick.AddListener(ClickCloseBtn);roleView.btnLevUp.onClick.AddListener(ClickLevUpBtn);//PlayerModel.Data.AddEventListener(roleView.UpdateInfo);PlayerModel.Data.AddEventListener(UpdateInfo);}private void ClickCloseBtn(){HideMe();}private void ClickLevUpBtn(){//通过数据模块 进行升级 达到数据改变PlayerModel.Data.LevUp();}// Update is called once per frameprivate void UpdateInfo(PlayerModel data){if(roleView ! null){//直接在p中得到V界面的控件断开M和V的联系roleView.txtLev.text LV. data.Lev;roleView.txtHp.text data.Hp.ToString();roleView.txtAtk.text data.Atk.ToString();roleView.txtDef.text data.Def.ToString();roleView.txtCrit.text data.Crit.ToString();roleView.txtMiss.text data.Miss.ToString();roleView.txtLuck.text data.Luck.ToString();} }private void OnDestroy() {PlayerModel.Data.RemoveEventListener(UpdateInfo);}
}MVP同样由缺点Presenter中的逻辑很多。
但是之后逻辑修改也只用在Presenter中。
MVVM
全称为模型(Model)-视图(View)-视图模型(ViewModel)Model提供数据View负责界面ViewModel负责逻辑的处理 MVVM的由来是MVP(Model-View-Presenter)模式与WPF结合应用时发展演变过来的一种新型框架 数据绑定
将一个用户界面元素(控件)的属性 绑定到 一个类型(对象)实例上的某个属性的方法。
如果开发者有一个MainViewMode类型的实例那么他就可以把MainViewMode的“Lev”属性绑定到一个Ul中Text的“Text”属性上。“绑定”了这2个属性之后对Text的Text属性的更改将“传播”到MainViewMode的Lev属性而对MainViewMode的Lev属性的更改同样会“传播”到Text的Text属性
MVVM在Unity中水土不服
因为View对象始终由我们来书写没有UI配置文件如WPF中的XAML要想实现传播需要事件/委托很麻烦。
硬要在Unity中实现MVVM需要写三模块并且还要对V和VM进行数据绑定工作量大好处也不够明显
Unity的第三方MVVM框架Loxodon Framework https://github.com/vovgou/loxodon-frameworkuMVVM https://github.com/MEyes/uMVVM MVE
全称为模型(Model)-视图(View)-事件中心(EventCenter) Model提供数据View负责界面EventCenter负责数据传递 好处
利用事件中心的观察者模式 让M和V层的之间的关系更加灵活多变减少了目前数据层的负载 将数据层事件全部交由事件中心处理
总结 铁打的M和V流水的X 数据和界面是必备的内容 我们可以通过改变X元素来优化原本的MVC 也就是改变联系和处理M(数据)和V(界面)的方式
不要拘泥于框架结构和设计模式要找到一个适合自己项目的 一个稳定的有序的能满足项目需求的实现方式