游戏UI框架设计(二)
--最简版本设计
为降低难度决定先讲解一个最简版本,阐述UI框架的核心设计理念。这里先定义三个核心功能:
1:UI窗体的自动加载功能。
2:缓存UI窗体。
3:窗体生命周期(状态)管理。
UI框架设计主要目的,就是尽可能的完成一些与具体游戏功能逻辑无关的一些底层事务性的功能实现。这些功能最好是自动或者是半自动的实现,无须客户程序(调用框架的程序)再去过多处理与关心。
对于以上功能,笔者定义了UI框架的相关四个核心类:
- BaseUIForms 基础UI窗体脚本(父类,其他窗体都继承此脚本)
- UIManger.cs UI窗体管理器脚本(框架核心脚本)
- UIType 窗体类型 (引用窗体的重要属性[枚举类型])
- SysDefine 系统定义类(包含框架中使用到的枚举类型、委托事件、系统常量、接口等)
在SysDefine 文件中,定义本框架三个核心枚举类型
1 //UI窗体(位置)类型 2 public enum UIFormType 3 { 4 //普通窗体 5 Normal, 6 //固定窗体 7 Fixed, 8 //弹出窗体 9 PopUp 10 } 11 12 //UI窗体的显示类型 13 public enum UIFormShowMode 14 { 15 //普通 16 Normal, 17 //反向切换 18 ReverseChange, 19 //隐藏其他 20 HideOther 21 } 22 23 //UI窗体透明度类型 24 public enum UIFormLucenyType 25 { 26 //完全透明,不能穿透 27 Lucency, 28 //半透明,不能穿透 29 Translucence, 30 //低透明度,不能穿透 31 ImPenetrable, 32 //可以穿透 33 Pentrate 34 }
上述三个核心枚举类型,解释如下:
- UIFormType 枚举类型,表示Unity层级视图中挂载不同类型窗体的空节点。这里Fixed 表示固定窗体,表示可以挂载"非全屏非弹出窗体",例如RPG游戏项目中的“英雄信息”窗体等。
- UIFormShowMode 枚举,表示窗体不同的显示方式。Normal 类型表示窗体与其他窗体可以并列显示; HideOther类型表示窗体显示的时候,需要隐藏所有其他窗体; ReverseChange 窗体主要应用与"弹出窗体",维护多个弹出窗体的层级关系。
- UIFormLucenyType 枚举,是定义弹出“模态窗体”不同透明度的类型。
上图是我们定义的UGUI 中的“根窗体”预设 "Canvas",在Untiy的层级视图中,可以看到我们定义了若干空节点,用于不同类型的UI窗体加载到不同的“根窗体”预设中,实现不同显示效果。
定义 UIType 类,主要是引用定义的三个核心枚举,方便使用 。代码如下:
1 /// <summary> 2 /// UI(窗体)类型 3 /// </summary> 4 internal class UIType 5 { 6 //是否需要清空“反向切换” 7 public bool IsClearReverseChange = false; 8 //UI窗体类型 9 public UIFormsType UIForms_Type = UIFormsType.Normal; 10 //UI窗体显示类型 11 public UIFormsShowMode UIForms_ShowMode = UIFormsShowMode.Normal; 12 //UI窗体透明度类型 13 public UIFormsLucencyType UIForms_LucencyType = UIFormsLucencyType.Lucency; 14 }
定义基础UI窗体 BaseUIForms 脚本,代码如下:
1 public class BaseUIForms : MonoBehaviour 2 { 3 /* 字段 */ 4 //当前(基类)窗口的类型 5 private UIType _CurrentUIType=new UIType(); 6 7 /* 属性 */ 8 /// <summary> 9 /// 属性_当前UI窗体类型 10 /// </summary> 11 internal UIType CurrentUIType 12 { 13 set 14 { 15 _CurrentUIType = value; 16 } 17 18 get 19 { 20 return _CurrentUIType; 21 } 22 } 23 24 //页面显示 25 public virtual void Display() 26 { 27 this.gameObject.SetActive(true); 28 } 29 30 //页面隐藏(不在“栈”集合中) 31 public virtual void Hiding() 32 { 33 this.gameObject.SetActive(false); 34 } 35 //页面重新显示 36 public virtual void Redisplay() 37 { 38 this.gameObject.SetActive(true); 39 } 40 //页面冻结(还在“栈”集合中) 41 public virtual void Freeze() 42 { 43 this.gameObject.SetActive(true); 44 } 45 46 }//Class_end
上述代码中,主要定义了UI窗体基类的四个重要虚方法,分别对应窗体的打开显示、隐藏、重新显示、窗体冻结(即:窗体显示在其他窗体下面)。方便窗体在不同状态下,针对不同的行为进一步做处理操作。例如,当窗体为“隐藏”与“冻结”状态时,如果此窗体有针对远程服务的网络连接(Socket套接字)时,则需要关闭网络连接,以节省网络资源。
定义“UI管理器”(UIManager.cs) 脚本,这是UI框架中的核心脚本,主要负责UI窗体的加载、缓存、以及对于“UI窗体基类”的各种生命周期的操作(显示、隐藏、重新显示、冻结)。
1 public class UIManager : MonoBehaviour { 2 /* 字段 */ 3 private static UIManager _Instance = null; 4 //UI窗体预设路径(参数1:窗体预设名称,2:表示窗体预设路径) 5 private Dictionary<string, string> _DicFormsPaths; 6 //缓存所有UI窗体 7 private Dictionary<string, BaseUIForm> _DicALLUIForms; 8 //当前显示的UI窗体 9 private Dictionary<string, BaseUIForm> _DicCurrentShowUIForms; 10 //UI根节点 11 private Transform _TraCanvasTransfrom = null; 12 //全屏幕显示的节点 13 private Transform _TraNormal = null; 14 //固定显示的节点 15 private Transform _TraFixed = null; 16 //弹出节点 17 private Transform _TraPopUp = null; 18 //UI管理脚本的节点 19 private Transform _TraUIScripts = null; 20 21 22 /// <summary> 23 /// 得到实例 24 /// </summary> 25 /// <returns></returns> 26 public static UIManager GetInstance() 27 { 28 if (_Instance==null) 29 { 30 _Instance = new GameObject("_UIManager").AddComponent<UIManager>(); 31 } 32 return _Instance; 33 } 34 35 //初始化核心数据,加载“UI窗体路径”到集合中。 36 public void Awake() 37 { 38 //字段初始化 39 _DicALLUIForms=new Dictionary<string, BaseUIForm>(); 40 _DicCurrentShowUIForms=new Dictionary<string, BaseUIForm>(); 41 _DicFormsPaths=new Dictionary<string, string>(); 42 //初始化加载(根UI窗体)Canvas预设 43 InitRootCanvasLoading(); 44 //得到UI根节点、全屏节点、固定节点、弹出节点 45 _TraCanvasTransfrom = GameObject.FindGameObjectWithTag(SysDefine.SYS_TAG_CANVAS).transform; 46 _TraNormal = _TraCanvasTransfrom.Find("Normal"); 47 _TraFixed = _TraCanvasTransfrom.Find("Fixed"); 48 _TraPopUp = _TraCanvasTransfrom.Find("PopUp"); 49 _TraUIScripts = _TraCanvasTransfrom.Find("_ScriptMgr"); 50 //把本脚本作为“根UI窗体”的子节点。 51 this.gameObject.transform.SetParent(_TraUIScripts, false); 52 //"根UI窗体"在场景转换的时候,不允许销毁 53 DontDestroyOnLoad(_TraCanvasTransfrom); 54 //初始化“UI窗体预设”路径数据 55 //先写简单的,后面我们使用Json做配置文件,来完善。 56 if (_DicFormsPaths!=null) 57 { 58 _DicFormsPaths.Add("LogonUIForm", @"UIPrefabsLogonUIForm"); 59 } 60 } 61 62 /// <summary> 63 /// 显示(打开)UI窗体 64 /// 功能: 65 /// 1: 根据UI窗体的名称,加载到“所有UI窗体”缓存集合中 66 /// 2: 根据不同的UI窗体的“显示模式”,分别作不同的加载处理 67 /// </summary> 68 /// <param name="uiFormName">UI窗体预设的名称</param> 69 public void ShowUIForms(string uiFormName) 70 { 71 BaseUIForm baseUIForms=null; //UI窗体基类 72 73 //参数的检查 74 if (string.IsNullOrEmpty(uiFormName)) return; 75 //根据UI窗体的名称,加载到“所有UI窗体”缓存集合中 76 baseUIForms = LoadFormsToAllUIFormsCatch(uiFormName); 77 if (baseUIForms == null) return; 78 //根据不同的UI窗体的显示模式,分别作不同的加载处理 79 switch (baseUIForms.CurrentUIType.UIForms_ShowMode) 80 { 81 case UIFormShowMode.Normal: //“普通显示”窗口模式 82 //把当前窗体加载到“当前窗体”集合中。 83 LoadUIToCurrentCache(uiFormName); 84 break; 85 case UIFormShowMode.ReverseChange: //需要“反向切换”窗口模式 86 //更靠后课程进行讲解。 87 break; 88 case UIFormShowMode.HideOther: //“隐藏其他”窗口模式 89 //更靠后课程进行讲解。 90 break; 91 default: 92 break; 93 } 94 } 95 96 #region 私有方法 97 //初始化加载(根UI窗体)Canvas预设 98 private void InitRootCanvasLoading() 99 { 100 ResourcesMgr.GetInstance().LoadAsset(SysDefine.SYS_PATH_CANVAS, false); 101 } 102 103 /// <summary> 104 /// 根据UI窗体的名称,加载到“所有UI窗体”缓存集合中 105 /// 功能: 检查“所有UI窗体”集合中,是否已经加载过,否则才加载。 106 /// </summary> 107 /// <param name="uiFormsName">UI窗体(预设)的名称</param> 108 /// <returns></returns> 109 private BaseUIForm LoadFormsToAllUIFormsCatch(string uiFormsName) 110 { 111 BaseUIForm baseUIResult = null; //加载的返回UI窗体基类 112 113 _DicALLUIForms.TryGetValue(uiFormsName, out baseUIResult); 114 if (baseUIResult==null) 115 { 116 //加载指定名称的“UI窗体” 117 baseUIResult = LoadUIForm(uiFormsName); 118 } 119 120 return baseUIResult; 121 } 122 123 /// <summary> 124 /// 加载指定名称的“UI窗体” 125 /// 功能: 126 /// 1:根据“UI窗体名称”,加载预设克隆体。 127 /// 2:根据不同预设克隆体中带的脚本中不同的“位置信息”,加载到“根窗体”下不同的节点。 128 /// 3:隐藏刚创建的UI克隆体。 129 /// 4:把克隆体,加入到“所有UI窗体”(缓存)集合中。 130 /// 131 /// </summary> 132 /// <param name="uiFormName">UI窗体名称</param> 133 private BaseUIForm LoadUIForm(string uiFormName) 134 { 135 string strUIFormPaths = null; //UI窗体路径 136 GameObject goCloneUIPrefabs = null; //创建的UI克隆体预设 137 BaseUIForm baseUiForm=null; //窗体基类 138 139 140 //根据UI窗体名称,得到对应的加载路径 141 _DicFormsPaths.TryGetValue(uiFormName, out strUIFormPaths); 142 //根据“UI窗体名称”,加载“预设克隆体” 143 if (!string.IsNullOrEmpty(strUIFormPaths)) 144 { 145 goCloneUIPrefabs = ResourcesMgr.GetInstance().LoadAsset(strUIFormPaths, false); 146 } 147 //设置“UI克隆体”的父节点(根据克隆体中带的脚本中不同的“位置信息”) 148 if (_TraCanvasTransfrom != null && goCloneUIPrefabs != null) 149 { 150 baseUiForm = goCloneUIPrefabs.GetComponent<BaseUIForm>(); 151 if (baseUiForm == null) 152 { 153 Debug.Log("baseUiForm==null! ,请先确认窗体预设对象上是否加载了baseUIForm的子类脚本! 参数 uiFormName=" + uiFormName); 154 return null; 155 } 156 switch (baseUiForm.CurrentUIType.UIForms_Type) 157 { 158 case UIFormType.Normal: //普通窗体节点 159 goCloneUIPrefabs.transform.SetParent(_TraNormal, false); 160 break; 161 case UIFormType.Fixed: //固定窗体节点 162 goCloneUIPrefabs.transform.SetParent(_TraFixed, false); 163 break; 164 case UIFormType.PopUp: //弹出窗体节点 165 goCloneUIPrefabs.transform.SetParent(_TraPopUp, false); 166 break; 167 default: 168 break; 169 } 170 171 //设置隐藏 172 goCloneUIPrefabs.SetActive(false); 173 //把克隆体,加入到“所有UI窗体”(缓存)集合中。 174 _DicALLUIForms.Add(uiFormName, baseUiForm); 175 return baseUiForm; 176 } 177 else 178 { 179 Debug.Log("_TraCanvasTransfrom==null Or goCloneUIPrefabs==null!! ,Plese Check!, 参数uiFormName="+uiFormName); 180 } 181 182 Debug.Log("出现不可以预估的错误,请检查,参数 uiFormName="+uiFormName); 183 return null; 184 }//Mehtod_end 185 186 /// <summary> 187 /// 把当前窗体加载到“当前窗体”集合中 188 /// </summary> 189 /// <param name="uiFormName">窗体预设的名称</param> 190 private void LoadUIToCurrentCache(string uiFormName) 191 { 192 BaseUIForm baseUiForm; //UI窗体基类 193 BaseUIForm baseUIFormFromAllCache; //从“所有窗体集合”中得到的窗体 194 195 //如果“正在显示”的集合中,存在整个UI窗体,则直接返回 196 _DicCurrentShowUIForms.TryGetValue(uiFormName, out baseUiForm); 197 if (baseUiForm != null) return; 198 //把当前窗体,加载到“正在显示”集合中 199 _DicALLUIForms.TryGetValue(uiFormName, out baseUIFormFromAllCache); 200 if (baseUIFormFromAllCache!=null) 201 { 202 _DicCurrentShowUIForms.Add(uiFormName, baseUIFormFromAllCache); 203 baseUIFormFromAllCache.Display(); //显示当前窗体 204 } 205 } 206 207 #endregion 208 209 }//class_end
UI管理器脚本解释如下:
一:上述代码中重要字段的解释如下:
1: “_DicFormsPaths” 表示“UI窗体预设路径”集合,负责缓存所有UI窗体预设的名称与对应资源路径的关系。
2: “ _DicALLUIForms” 表示“所有UI窗体”集合,负责缓存已经加载过的所有UI窗体名称以及与之对应的UI窗体。
3: “_DicCurrentShowUIForms”表示“当前正在显示”集合,负责控制正在显示UI窗体的内部逻辑。
4: UI管理器脚本中的“_TraCanvasTransfrom”、“_TraNormal”、“_TraFixed”、“_TraPopUp”、“_TraUIScripts”,分别表示Unity层级视图中的根结点、普通节点、固定节点、弹出节点、管理脚本节点,这些节点是加载UI窗体的不同类型的父节点,用于各种UI窗体的管理工作。
二:上述代码中重要方法的解释如下:
1: ShowUIForms() 是外部程序调用本框架的对外公共方法,负责加载、缓存、打开与显示制定窗体名称的UI窗体预设。
2: LoadFormsToAllUIFormsCatch() 是根据UI窗体的名称,加载到“所有UI窗体”缓存集合中。
3: LoadUIToCurrentCache() 是把当前窗体加载到“当前窗体”集合中。
上述(UI框架)脚本编写完毕,测试成功后效果如下图:
为广大读者进一步了解与熟悉本框架,特提供下载链接:https://pan.baidu.com/s/1eTA8rHS 密码:4x6e
本篇就先写到这,下篇 "游戏UI框架设计(3)_窗体的层级管理" 继续。