-
Model(模型)表示应用程序核心(比如数据库记录列表)。
-
View(视图)显示数据(数据库记录)。
-
Controller(控制器)处理输入(写入数据库记录)。 (引自百度)
- 打开,关闭,层级,页面跳转等管理问题集中化,将外部切换等逻辑交给UIManager处理
- 功能逻辑分散化,每个页面维护自身逻辑,依托于框架便于多人协同开发,不用关心跳转和显示关闭细节
- 通用性框架能够做到简单的代码复用和"项目经验"沉淀
耦合性低 重用性高 可维护性高 生命周期成本低 有利软件工程化管理 部署快
缺点:
不适合小型,中等规模的应用程序 增加系统结构和实现的复杂性 视图与控制器间的过于紧密的连接 视图对模型数据的低效率访问
(以奇怪大冒险手游为例)
抽象Model层:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// UI派生类的基类,我们可以写一些通用方法
public class UIBase : MonoBehaviour
{
// 用户界面第一次进入屏幕调用
public virtual void UserInterfaceEnter()
{
}
// 用户界面在当前屏幕停止时调用
public virtual void UserInterfacePause()
{
}
// 用户界面重新回到屏幕时调用
public virtual void UserInterfaceResume()
{
}
// 用户界面销毁时调用
public virtual void UserInterfaceDestroy()
{
}
public virtual void UserInterfaceExit()
{
}
}
UI的管理类,控制UI的出栈入栈和暂停恢复:
// 因为我们需要经常调用UIManager类中的方法,并且UIManager不允许存在多个对象,只能有有一个对象,所以使用单例
private static UIManager _instance;
public static UIManager Instance
{
get
{
return _instance;
}
}
// 创建一个泛型栈,用来管理UI入栈出栈
private static Stack<UIBase> UIStack = new Stack<UIBase>();
// 字典来缓存已经实例化的UI面板对象
private static Dictionary<string, UIBase> CacheDict = new Dictionary<string, UIBase>();
// 创建另一个字典来管理我们所有的UI预设体
private static Dictionary<string, GameObject> AllObjectDict = new Dictionary<string, GameObject>();
// 在Awake方法中需要指定单例
private void Awake()
{
// 执行单例对象
_instance = this; // 实例化
// 将我们的预设体都添加入字典
AddUIPanel("UIStart");
AddUIPanel("UIOption");
AddUIPanel("PlayScene");
}
// 添加UI面板中的预设体
private void AddUIPanel(string name) // 根据名字添加预设体
{
// 要先拿到预设体
// 拿到路径,也就是我们存储预设体的路径
string uiPath = "UI/" + name;
// 通过Resources 类加载Resources/UI下的预设体
GameObject uiObj = Resources.Load<GameObject>(uiPath);
// 判断是否为空
if (uiObj)
{
// 将预设体添加到字典,名字作为键,预设体作为值
AllObjectDict.Add(name, uiObj);
}
}
// 入栈
public void PushUIPanel(string uiName)
{
// 判断栈中是否为空
if (UIStack.Count > 0)
{
// 返回栈顶元素
UIBase currentTop = UIStack.Peek();
// 暂停UI
currentTop.OnPause();
}
// 添加新的元素到栈顶
UIBase newTop = GetNewElement(uiName);
// 入栈的时候调用 UIBase 入栈方法
newTop.OnEnter();
UIStack.Push(newTop);
}
// 获取UIBase的方法
private UIBase GetNewElement(string uiname)
{
// 首先需要判断缓存区是否已经有了缓存,如果有那么直接将缓存区的返回,没有的话添加入缓存区并返回
foreach (var item in CacheDict.Keys)
{
// 如果我们需要添加的栈顶名字缓存区有的话那么直接取出
if (item == uiname)
{
return CacheDict[uiname];
}
}
GameObject go = AllObjectDict[uiname];
// 其次创建 go
GameObject go1 = Instantiate(go);
// 让我们创建出的名字和设定的一样
go1.name = uiname;
// 拿到他身上的组件
UIBase uibase = go1.GetComponent<UIBase>();
// 添加入缓存区
CacheDict.Add(uiname, uibase);
return uibase;
}
// 出栈
public void PopUIStack()
{
// 判断栈中是否有元素
if (UIStack.Count == 0)
{
return;
}
// 取出栈顶并且移除
UIBase currentTop = UIStack.Pop();
// 执行退出方法
currentTop.OnExit();
if (UIStack.Count > 0)
{
// 取出当前栈顶元素,并执行恢复UI方法
UIBase newTop = UIStack.Peek();
newTop.OnResume();
}
}
控制选项界面:
public override void OnEnter()
{
// 使当前的UI摄像机为我们的主摄像机
GetComponent<Canvas>().worldCamera = Camera.main;
gameObject.SetActive(true);
canvasG.interactable = true;
canvasG.DOFade(1f, 0.3f);
}
public override void OnPause()
{
canvasG.interactable = false;
}
public override void OnResume()
{
canvasG.interactable = true;
}
public override void OnExit()
{
canvasG.DOFade(0, 0.3f);
canvasG.interactable = false;
gameObject.SetActive(false);
}
public void PushStart()
{
// 移除栈顶
UIManager.Instance.PopUIStack();
}
}
控制开始界面:
{
public override void OnEnter()
{
// 使当前的UI摄像机为我们的主摄像机
GetComponent<Canvas>().worldCamera = Camera.main;
AudioManager.Instance.PlayAudio("Audio_bgm_1");
}
public override void OnPause()
{
// 禁用UI的交互功能
canvasG.interactable = false;
}
public override void OnResume()
{
canvasG.interactable = true;
}
public override void OnExit()
{
base.OnExit();
}
public void PushOption()
{
// 添加栈顶
UIManager.Instance.PushUIPanel("UIOption");
}
public void IsMute(bool mute)
{
AudioManager.Instance.Mute = mute;
}
public void PushPlayScene() {
// 添加游戏界面为栈顶
UIManager.Instance.PushUIPanel("PlayScene");
}
}
管理音乐的类:
{
private AudioManager()
{
}
private static AudioManager _instance;
public static AudioManager Instance
{
get
{
return _instance;
}
}
private void Awake()
{
_instance = this;
audioSource = GetComponent<AudioSource>();
// 设置
audioSource.playOnAwake = true;
audioSource.loop = true;
}
AudioSource audioSource;
string audioDir = "Audios";
AudioClip currentClip;
Dictionary<string, AudioClip> AllClipDict = new Dictionary<string, AudioClip>();
// 播放一个北京音乐
public void PlayBGM(string ACname)
{
AudioClip ac;
// 判断这首音乐已经加载到内存,是的话就获取出来,不是就从资源文件加当中加载
if (AllClipDict.ContainsKey(ACname))
{
ac = AllClipDict[ACname];
}
else
{
// 构建路径
string path = audioDir + "/" + ACname;
ac = Resources.Load<AudioClip>(path);
AllClipDict.Add(ACname, ac);
}
currentClip = ac;
audioSource.clip = ac;
audioSource.Play();
}
public void StopBGM()
{
audioSource.Stop();
}
public void ResumeBGM()
{
audioSource.Play();
}
// 调节音量大小
public float Volumn
{
set
{
audioSource.volume = value;
}
get
{
return audioSource.volume;
}
}
Dictionary<string, AudioClip> AllBGmDict = new Dictionary<string, AudioClip>();
// 音频(一小段)
public void PlayClip(string name)
{
AudioClip ac;
if (AllBGmDict.ContainsKey(name))
{
ac = AllBGmDict[name];
}
else
{
// 构建路径
string path = audioDir + "/" + name;
ac = Resources.Load<AudioClip>(path);
AllBGmDict.Add(name, ac);
}
AudioSource.PlayClipAtPoint(ac, Vector3.zero);
}
}
启动UI的类:
public class GameController : MonoBehaviour {
void Start () {
UIManager.Instance.PushUIInterface("UIStart");
AudioManager.Instance.PlayBGM("Audio_bgm_1");
}
}