为什么要使用UI框架呢?

在我刚使用Unity开发UI界面时,根本没想过用什么UI框架,都是想到要什么界面就通过UGUI拖动什么界面。如果需要实现交互功能,就会绑定对应的监听函数,这样的做法固然是非常的简单直接,但是也会留下一定的弊端。

当你的项目不在简单时,UI界面和控件越来越多时,你有时候会找不到哪个对象和哪个对象关联,要是团队合作的话,别人找你的UI接口更是找半天,耦合度非常之高,经常会牵一发而动全身,代码混乱,所以这个时候有一个好的UI框架,会更便于我们整合和使用UI。

设计UI框架的要点

1.设计一个UI的基类BaseUI,里面有UI的一些基本属性,显示方式等,所有UI的脚本都继承它
2.设计一个管理UI的框架UIManager,为UI的显示、销毁、切换等提供方法接口,并把该脚本设为单例模式,每个UI间互不干扰
3.UI间通信、UI和游戏层的通信通过一个消息分发的脚本去实现,即利用观察者模式,都通过MessageCenter来联系

部分伪代码:

建立一个定义UI类型的脚本

     //窗体IDpublic enum E_UiId{MainUI,//主界面PlayUI,//游戏界面}//窗体的层级public enum E_UIRootType{KeepAbove,//  保持在最前方的窗体Normal//  普通窗体}//窗体的显示方式public enum E_ShowUIMode{//  窗体显示出来的时候,不会去隐藏任何窗体DoNothing,//  窗体显示出来的时候,会隐藏掉所有的普通窗体,但是不会隐藏保持在最前方的窗体HideOther,//  窗体显示出来的时候,会隐藏所有的窗体,不管是普通的还是保持在最前方的HideAll}//  消息类型public enum E_MessageType{ImageChange,//  图片更换textChange,//  文字更换RewardChange,// 奖励更换WeaponChange,// 武器更换BulletTips// 弹药提示}public class GameDefine{//  导入UI预制体文件(名字,路径)public static Dictionary<E_UiId, string> dicPath = new Dictionary<E_UiId, string>(){{ E_UiId.MainUI,"UIPrefab/"+"MainPanel"},{ E_UiId.PlayUI,"UIPrefab/"+"PlayPanel"}};//  自动挂载UI脚本public static Type GetUIScriptType(E_UiId uiId){Type scriptType = null;switch (uiId){case E_UiId.NullUI:break;case E_UiId.MainUI:scriptType = typeof(MainUI);break;case E_UiId.PlayUI:scriptType = typeof(PlayUI);break;default:break;}return scriptType;}}

定义UI基类

//窗体类型public class UIType{//显示方式public E_ShowUIMode showMode = E_ShowUIMode.HideOther;//父节点的类型public E_UIRootType uiRootType = E_UIRootType.Normal;}public class BaseUI : MonoBehaviour{//窗体类型public UIType uiType;//缓存窗体的RectTransform组件protected RectTransform thisTrans;//当前窗体的IDprotected E_UiId uiId = E_UiId.NullUI;//上一个窗体的IDprotected E_UiId beforeUiId = E_UiId.NullUI;//获取当前窗体的IDpublic E_UiId GetUiId{get{return uiId;}//为什么没有set?//因为每个窗体的ID都是固定的,不能被外界随意修改,外界只能获取它的值//只有在子类才能对该窗体的ID进行赋值或修改//set//{//    uiId = value;//}}protected virtual void Awake(){if (uiType == null){uiType = new UIType();}thisTrans = this.GetComponent<RectTransform>();InitUiOnAwake();InitDataOnAwake();}//用于判断窗体显示出来的时候,是否需要去隐藏其他窗体public bool IsHideOtherUI(){if (this.uiType.showMode == E_ShowUIMode.DoNothing){return false;//不需要隐藏其他窗体}else{//需要去处理隐藏其他窗体的逻辑return true;// E_ShowUIMode.HideOther与  E_ShowUIMode.HideAll}}//初始化界面元素protected virtual void InitUiOnAwake(){}//初始化数据protected virtual void InitDataOnAwake(){}protected virtual void Start(){InitOnStart();}//初始化相关逻辑protected virtual void InitOnStart(){}//窗体的显示public virtual void ShowUI(){this.gameObject.SetActive(true);}//窗体额隐藏public virtual void HideUI(Del_AfterHideUI del = null){this.gameObject.SetActive(false);if (del != null){del();}}

定义UIManager框架

//这里我把单例模式写成了一个基类UnitySingleton并继承
public class UIManager : UnitySingleton<UIManager>
{//缓存所有打开过的窗体private Dictionary<E_UiId, BaseUI> dicAllUI;//缓存正在显示的窗体private Dictionary<E_UiId, BaseUI> dicShowUI;//缓存最近显示出来的窗体private BaseUI currentUI = null;//缓存上一个窗体// private BaseUI beforeUI = null;private E_UiId beforeUiId = E_UiId.NullUI;//缓存画布private Transform canvas;//缓存保持在最前方的窗体的父节点private Transform keepAboveUIRoot;//缓存普通窗体的父节点private Transform normalUIRoot;private void Awake(){//Test.Instance.Show();dicAllUI = new Dictionary<E_UiId, BaseUI>();dicShowUI = new Dictionary<E_UiId, BaseUI>();InitUIManager();}//初始化UI管理类private void InitUIManager(){canvas = this.transform.parent;//设置画布在场景切换的时候不被销毁,因为整个游戏共用唯一的一个画布DontDestroyOnLoad(canvas);if (keepAboveUIRoot==null){keepAboveUIRoot = GameTool.FindTheChild(canvas.gameObject, "KeepAboveUIRoot");}if (normalUIRoot==null){normalUIRoot= GameTool.FindTheChild(canvas.gameObject, "NormalUIRoot");}}//供外界调用,销毁窗体public void DestroyUI(E_UiId uiId){if (dicAllUI.ContainsKey(uiId)){//存在该窗体,去销毁Destroy( dicAllUI[uiId].gameObject);dicAllUI.Remove(uiId);}}//供外界调用的,显示窗体的方法public BaseUI ShowUI(E_UiId uiId,bool isSaveBeforeUiId=true){if (uiId == E_UiId.NullUI){uiId = E_UiId.MainUI;}BaseUI baseUI=JudgeShowUI(uiId);if (baseUI!=null){baseUI.ShowUI();}if (isSaveBeforeUiId){baseUI.BeforeUiId = beforeUiId;}return baseUI;}//供外界调用,反向切换窗体的方法public void ReturnBeforeUI(E_UiId uiId){ShowUI(uiId,false);}//供外界调用的,隐藏单个窗体的方法public void HideSingleUI(E_UiId uiId, Del_AfterHideUI del=null){if (!dicShowUI.ContainsKey(uiId)){return; }dicShowUI[uiId].HideUI(del);dicShowUI.Remove(uiId);}private BaseUI JudgeShowUI(E_UiId uiId){//判断将要显示的窗体是否已经正在显示了if (dicShowUI.ContainsKey(uiId)){//如果已经正在显示了,就不需要处理其他逻辑了return null;}//判断窗体是否有加载过BaseUI baseUI = GetBaseUI(uiId);if (baseUI == null){//说明这个窗体没显示过(没有加载过),要去动态加载string path = GameDefine.dicPath[uiId];GameObject theUI = Resources.Load<GameObject>(path);if (theUI != null){//把该窗体生成出来GameObject willShowUI = Instantiate(theUI);//窗体生成出来后,要确保有挂对应的UI脚本baseUI = willShowUI.GetComponent<BaseUI>();if (baseUI == null){//说明生成出来的这个窗体上面没有挂载对应的UI脚本//那么就需要给这个窗体自动添加对应的脚本Type type = GameDefine.GetUIScriptType(uiId);baseUI = willShowUI.AddComponent(type) as BaseUI;}//判断这个窗体是属于哪个父节点的Transform uiRoot = GetTheUIRoot(baseUI);GameTool.AddChildToParent(uiRoot, willShowUI.transform);willShowUI.GetComponent<RectTransform>().sizeDelta = Vector2.zero;//这个窗体是第一次加载显示出来的,那么就需要缓存起来dicAllUI.Add(uiId, baseUI);}else{Debug.LogError("指定路径下面找不到对应的预制体");}}UpdateDicShowUIAndHideUI(baseUI);return baseUI;}//更新缓存正在显示的窗体的字典并且隐藏对应的窗体private void UpdateDicShowUIAndHideUI(BaseUI baseUI){//判断是否需要隐藏其他窗体if (baseUI.IsHideOtherUI()){//如果返回值为true, E_ShowUIMode.HideOther与 E_ShowUIMode.HideAll//需要隐藏其他窗体if (dicShowUI.Count>0){//有窗体正在显示,就要隐藏对应的窗体if (baseUI.uiType.showMode == E_ShowUIMode.HideOther){HideAllUI(false, baseUI);}else{HideAllUI(true, baseUI);}}}//更新缓存正在显示的窗体的字典dicShowUI.Add(baseUI.GetUiId, baseUI);}public void HideAllUI(bool isHideAboveUI,BaseUI baseUI){               if (isHideAboveUI){//1、隐藏所有的窗体,不管是普通窗体还是保持在最前方的窗体,都需要全部隐藏foreach (KeyValuePair<E_UiId,BaseUI> uiItem in dicShowUI){uiItem.Value.HideUI(); }dicShowUI.Clear();}else{//2、隐藏所有的窗体,但是不包含保持在最前方的窗体//缓存所有被隐藏的窗体List<E_UiId> list = new List<E_UiId>();foreach (KeyValuePair<E_UiId, BaseUI> uiItem in dicShowUI){//如果不是保持在最前方的窗体if (uiItem.Value.uiType.uiRootType != E_UIRootType.KeepAbove){uiItem.Value.HideUI();//存储上一个窗体的IDbeforeUiId = uiItem.Key;// baseUI.BeforeUiId= uiItem.Key;list.Add(uiItem.Key);}}for (int i = 0; i < list.Count; i++){dicShowUI.Remove(list[i]);}}}//判断窗体的父物体private Transform GetTheUIRoot(BaseUI baseUI){if (baseUI.uiType.uiRootType == E_UIRootType.KeepAbove){return keepAboveUIRoot;}else{return normalUIRoot;}}private BaseUI GetBaseUI(E_UiId UiId){if (dicAllUI.ContainsKey(UiId)){return dicAllUI[UiId];}else{return null;}}
}

定义通讯中心MessageCenter

public class MessageCenter : MonoBehaviour
{public delegate void CallBack(object obj);//一个用来存放所有监听的字典<消息类型,监听到消息后所要处理的逻辑>public static Dictionary<E_MessageType, CallBack> dicMessageType = new Dictionary<E_MessageType, CallBack>();//添加监听public static void AddMessageListener(E_MessageType messageType, CallBack callBack){if (!dicMessageType.ContainsKey(messageType)){dicMessageType.Add(messageType, null);}dicMessageType[messageType] += callBack;}//取消监听public static void RemoveListener(E_MessageType messageType, CallBack callBack){if (dicMessageType.ContainsKey(messageType)){dicMessageType[messageType] -= callBack;}}//取消所有的监听public static void RemoveAllMessage(){dicMessageType.Clear();}//广播消息public static void SendMessage(E_MessageType messageType, object obj = null){CallBack callBack;if (dicMessageType.TryGetValue(messageType, out callBack)){if (callBack != null){callBack(obj);}}}
}

以上就是一个基本的UI框架了,接下来只要自己去创建对应的UI界面并处理好继承问题和初始化一些基础的UI属性即可,其他的一些UI框架其实也万变不离其中,主要目的还是解耦,便于处理,如果有好用的框架欢迎私信或者底下留言我,大家一起分享!

链接: 看到一位已经转行做厨师的兄弟有感而发

Unity UI框架的搭建相关推荐

  1. unity ui框架_[教程汇总+持续更新]Unity从入门到入坟——收藏这一篇就够了

    ----------------塔防(更新中),作者重写了基础篇(下方目录为:1.1(新) 基础)目前还在持续连载了5篇,因为不多我们更新完就能追到原作者的进度了------------------- ...

  2. Android QQ空间(Apad)项目总结(三)---应用UI框架的搭建!!!

    大家好,今天是元旦节了,祝大家节日快乐!今天给大家分享的是Apad Qzone的UI框架,我们首先看下交互图如下: 图1:交互效果图. 从上图可以看出,整个应用其实UI框架相对比较简单,可以分为俩部分 ...

  3. (转)Android QQ空间(Apad)项目总结(三)---应用UI框架的搭建!!!

    今天给大家分享的是Apad Qzone的UI框架,我们首先看下交互图如下: 图1:交互效果图. 从上图可以看出,整个应用其实UI框架相对比较简单,可以分为俩部分,左侧导航栏区域,右侧显示内容区域.当我 ...

  4. unity ui框架_用unity制作简单的太空游戏(2)-简单炮台

    多铆蒸刚,炮塔至大! 亿万星辰,亿万炮塔! 多铆蒸刚,炮塔至上! 亿万炮塔,亿万荣光! (PS:我没有咕咕咕,就是比较惨,一口气出了半个月的差,人瘦了,也黑了,心塞塞--赶紧写个文章压压惊--) 这一 ...

  5. Unity UI 框架

    开源地址: GitHub - NRatel/NRFramework.UI: 基于 Unity UGUI 的 UI 开发框架基于 Unity UGUI 的 UI 开发框架. Contribute to ...

  6. 微信小程序 - Vant weapp UI 框架环境搭建详细教程(Window)

    前言 强烈推荐您打开 官方文档,对照着本教程一起对比搭建坏境. 自从 2022 年开始,小程序做了很多改变和升级, 导致网上很多搭建教程文章的教程失效了,本文来做最新的教程. 第一步 为了更贴合新手, ...

  7. Unity UI框架

    窗口管理 初始化 在场景中挂载一个脚本,用于创建初始界面 using UnityEngine;//创建开始界面 public class CreateStartPanel : MonoBehaviou ...

  8. Unity UI框架详细理解--场景管理

    SceneState类:存放场景状态                          OnEnter方法:负责实行场景进入时候的方法 Onexit方法:负责实行场景退出时候的方法 脚本存放的位置: ...

  9. iOS之UI--主流框架的搭建-- 仿制QQ的UI框架

    iOS之UI--主流框架的搭建-- 仿制QQ的UI框架 使用XCode搭建多个控制器界面,一般在实际开发中建议超过四个控制器界面使用纯代码. 下面的实例其实已经超过了四个,总结详细步骤的目的,主要是更 ...

最新文章

  1. Android——RatingBar(评价条)相关知识总结贴
  2. Druid数据库连接池使用
  3. OCP大会 | 腾讯云Open DCN Networking(附PDF)
  4. 深度学习之循环神经网络(11-a)LSTM情感分类问题代码
  5. 建立数组并写入数据_VBA数组与字典解决方案第37讲:在VBA中字典的应用
  6. exchange2003系列总结:-5邮件加密与签名的工作流程
  7. 零基础入门AI量化交易学习笔记
  8. 硬盘被计算机限制如果解锁,硬盘被锁怎么办
  9. 手机电脑普通浏览器或UC浏览器缓存或下载的Y2hlbmppbmdjb25n0 Y2hlbmppbmdjb25n1 m3u8视频.ts格式视频合并工具成一个mp4
  10. 第一个Python程序--Python
  11. 计算机符号mi,在线特殊符号大全
  12. 普通带条件查询接口报错 Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError:
  13. java.library.path和LD_LIBRARY_PATH的介绍与区别
  14. Linux内存管理1---内存寻址
  15. android的视频直播,Android进行视频,直播播放
  16. 用计算机唱歌 丑八怪乐谱,得力计算器乐谱丑八怪 | 手游网游页游攻略大全
  17. 计算机网络 --- 计算机网络和因特网
  18. 非常精美的唐诗,无与伦比哦遥知兄弟登高处,遍插茱萸少一人。秦时明月汉时关,万里长征人未还。但使龙城飞将在,不教胡马度阴山。春眠不觉晓,处处闻啼鸟。夜来风雨声,花落知多少。君自故乡来,应知故乡事。来日绮
  19. 格兰仕滚筒洗衣机学习
  20. java输入和输出路径_Java输入输出

热门文章

  1. Redis从入门到精通
  2. LM75AD温度传感器的应用(1)
  3. bat中rar压缩命令
  4. 第二章第十六题(几何:六边形面积)(Geometry: area of a hexagon)
  5. 谁可以参与初创股权分配?
  6. php毕设代做,客户管理系统,java,jsp,php,好毕设为你指导如何完成一个客户管理系统...
  7. 信鸽推送集成采坑之代码混淆报错/XINGE: [Util] please add wup-1.0.0.E-SNAPSHOT.jar in your libs
  8. 电脑lol性能测试软件,lol电脑配置测试
  9. 直流电机驱动电路中L298与电机间二极管的作用
  10. 汪延谈王志东离职问题 (转)