分析: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预制体效果图:
运行效果图: