分析:UI框架主要是为了用户(使用框架的程序猿)更快捷、方便地开发UI,UI框架的好处还在于解耦,使得程序更具有灵活性。
UI框架的核心是窗口的管理,窗口管理的主要任务就是显示窗口和关闭窗口。
因为窗口的类型多样,比如弹出式窗口,固定位置窗口,隐藏其他窗口(打开这个窗口会覆盖整个屏幕),模态窗口等等。
这里我目前把窗口分为三大类型:普通窗口、弹出式窗口、隐藏其他窗口,而位置固定、是否模态作为窗口的属性。
1.为了更易于复用和拓展,我设计了一个基类BasePanel, NormalPanel, PopupPanel, HiderOtherPanel都由此基类派生。
BasePanel封装了3个重要方法:Open(), Close(), Freeze()。
2.巧妇难为无米之炊,要显示这些窗体首先要制作这些窗体的预制体,然后加载。为此,我又设计了一个核心类PanelManager。
PanelManager主要负责窗体的创建和销毁(隐藏),核心方法CreatePanel()。另外使用了对象池技术,缓存窗体。
3.要动态加载窗体,必须获得窗体资源的路径,为此我设计了一个SysDefine类,此文件用于定义一些预制体路径常量,节点(Inspector面板中的物体位置)常量。(后期重构时打算用Json文件配置)。加载资源的类ResourceManager。
4.此外,我还设计了一个帮助类Helper,目前实现的功能仅仅是自动化设置窗体节点的父节点。
下面,展示我今天下午的成果。
1 using UnityEngine; 2 3 public class BasePanel : MonoBehaviour 4 { 5 //窗体类型 6 public EPanelType panelType; 7 //是否是模态窗口 8 public bool isModal; 9 //是否是固定位置窗口 10 public bool isFixed; 11 //透明度 12 ETransparencyLevel transLevel; 13 14 15 //初始化 16 private void Awake() 17 { 18 panelType = EPanelType.Normal; 19 isModal = true; 20 isFixed = false; 21 transLevel = ETransparencyLevel.Opaque; 22 } 23 24 private void Start() 25 { 26 //自动设置物体的父节点 27 Helper.GetInstance().SetParent(gameObject); 28 29 } 30 31 //打开窗口 32 public void Open() 33 { 34 gameObject.SetActive(true); 35 } 36 //关闭窗口 37 public void Close() 38 { 39 gameObject.SetActive(false); 40 } 41 //冻结窗口 42 public void Freeze() 43 { 44 //TO DO 45 } 46 }
1 using System.Collections.Generic; 2 using UnityEngine; 3 public class PanelManager 4 { 5 //本类实例 6 private static PanelManager _instance; 7 //存储面板名字和对应的路径字典 8 public static Dictionary<string, string> dictPanelPath; 9 //存储已显示的面板字典 10 public static Dictionary<string, BasePanel> dictCurPanel; 11 //存储已隐藏的面板字典 12 public static Dictionary<string, BasePanel> dictHidePanel; 13 //存储Popup类型面板的字典 14 public static Dictionary<string, Stack<BasePanel>> dictPopupPanel; 15 16 //单例模式 17 private PanelManager() { } 18 public static PanelManager GetInstance() 19 { 20 if(_instance == null) 21 { 22 _instance = new PanelManager(); 23 24 InitProperties(); 25 } 26 return _instance; 27 } 28 //初始化字段 29 private static void InitProperties() 30 { 31 dictPanelPath = new Dictionary<string, string>(); 32 dictCurPanel = new Dictionary<string, BasePanel>(); 33 dictHidePanel = new Dictionary<string, BasePanel>(); 34 dictPopupPanel = new Dictionary<string, Stack<BasePanel>>(); 35 } 36 /// <summary> 37 /// 创建一个面板 38 /// 先检查dictHidePanel集合里是否存在此面板,有则取出显示并加入dictCurPanel集合 39 /// 没有,则创建一个,然后加如dictCurPanel集合。 40 /// </summary> 41 /// <param name="panelName">要创建的面板的名字</param> 42 /// <returns></returns> 43 public BasePanel CreatePanel(string panelName) 44 { 45 BasePanel basePanel = null; 46 dictHidePanel.TryGetValue(panelName, out basePanel); 47 if(basePanel != null) 48 { 49 return basePanel; 50 } 51 else 52 { 53 //创建面板 54 GameObject go = ResourceManager.GetInstance().LoadAsset<GameObject>(panelName); 55 if(go != null) 56 { 57 basePanel = go.GetComponent<BasePanel>(); 58 if(basePanel != null) 59 { 60 //添加到正在显示的面板集合 61 dictCurPanel.Add(panelName, basePanel); 62 } 63 else 64 { 65 Debug.LogError(GetType()+"你可能忘记挂在了BasePanel类型的脚本"); 66 } 67 return basePanel; 68 } 69 else 70 { 71 Debug.Log(GetType()+"panelName可能不存在"); 72 73 } 74 } 75 return null; 76 } 77 78 }
1 using UnityEngine; 2 3 public class ResourceManager 4 { 5 private static ResourceManager _instance; 6 public static ResourceManager GetInstance() 7 { 8 if (_instance == null) 9 { 10 _instance = new ResourceManager(); 11 } 12 return _instance; 13 } 14 private ResourceManager() { } 15 16 public T LoadAsset<T>(string path)where T:Object 17 { 18 Object o = Resources.Load(path); 19 //实例化 20 GameObject go = GameObject.Instantiate(o) as GameObject; 21 22 return go as T; 23 } 24 25 }
1 public enum ETransparencyLevel 2 { 3 Opaque, //不透明 4 Translucence, //半透明的 5 Transparent, //透明的 6 } 7 public enum EPanelType 8 { 9 Normal, 10 Popup, 11 HideOther 12 } 13 public class PrefabPathStr 14 { 15 public const string uiRootPath = @"Prefabs/UIRoot"; 16 public const string logOnPanelPath = @"Prefabs/LogOnPanel"; 17 } 18 public class NodePathStr 19 { 20 public const string normalPath = @"UIRoot/Normal"; 21 public const string popupPath = @"UIRoot/Popup"; 22 public const string hiderOtherPath = @"UIRoot/HideOther"; 23 } 24 25 26 27 public class SysDefine 28 { 29 }
1 using UnityEngine; 2 3 public class Helper 4 { 5 private static Helper _instance = null; //本类实; 6 private static Transform normal; //normal 节点 7 private static Transform popup; //popup 节点 8 private static Transform hiderOther; //hiderOther 节点 9 10 11 //单利模式 12 public static Helper GetInstance() 13 { 14 if(_instance == null) 15 { //本类实例化时,初始化节点字段 16 normal = GameObject.Find(NodePathStr.normalPath).transform; 17 popup = GameObject.Find(NodePathStr.popupPath).transform; 18 hiderOther = GameObject.Find(NodePathStr.hiderOtherPath).transform; 19 _instance = new Helper(); 20 } 21 return _instance; 22 } 23 private Helper() { } 24 /// <summary> 25 /// 若用户自定义了parent则使用,若没有则根据panelType自动设定。 26 /// </summary> 27 /// <param name="child">子物体</param> 28 /// <param name="parent">父物体</param> 29 public void SetParent(GameObject child, Transform parent = null) 30 { 31 if(parent != null) 32 { 33 child.transform.SetParent(parent, true); 34 child.transform.localScale = new Vector3(1, 1, 1); 35 child.transform.localPosition = Vector3.zero; 36 } 37 else 38 { 39 if(child.GetComponent<BasePanel>()!= null) 40 { 41 EPanelType panelType = child.GetComponent<BasePanel>().panelType; 42 switch (panelType) 43 { 44 case EPanelType.Normal: 45 child.transform.SetParent(normal); 46 break; 47 case EPanelType.Popup: 48 child.transform.SetParent(popup); 49 break; 50 case EPanelType.HideOther: 51 child.transform.SetParent(hiderOther); 52 break; 53 default: 54 Debug.LogError("错误,未知的窗体类型"); 55 break; 56 } 57 child.transform.localScale = new Vector3(1, 1, 1); 58 child.transform.localPosition = Vector3.zero; 59 } 60 else 61 { 62 Debug.LogError(GetType()+ "请检查此物体是否挂载了BasePanel类型脚本!"); 63 } 64 65 } 66 } 67 }
启动框架类StartGame.cs
1 using UnityEngine; 2 3 public class StartGame : MonoBehaviour 4 { 5 6 private GameObject uiRoot = null; 7 void Start() 8 { 9 uiRoot = GameObject.FindGameObjectWithTag("UIRoot"); 10 if (uiRoot != null) //已生成UIRoot 11 { 12 Destroy(uiRoot); 13 14 } 15 //获取UIRoot对象 16 uiRoot = ResourceManager.GetInstance().LoadAsset<GameObject>(PrefabPathStr.uiRootPath); 17 18 //修改克隆体的名字 19 uiRoot.name = "UIRoot"; 20 //加载场景时不销毁UIRoot 21 DontDestroyOnLoad(uiRoot); 22 23 //加载登陆面板 24 PanelManager.GetInstance().CreatePanel(PrefabPathStr.logOnPanelPath); 25 26 } 27 28 }
测试代码LogOnPanel.cs
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 public class LogOnPanel : BasePanel 6 { 7 private void Awake() 8 { 9 this.panelType = EPanelType.Normal; 10 } 11 12 }
UIRoot预制体效果图
LogOnPanel预制体效果图:
运行效果图: