Unity3d:UI面板管理整合进ToLua
本文基于
https://github.com/chiuan/TTUIFramework
https://github.com/jarjin/LuaFramework_UGUI
进行的二次开发,Thanks!
需求:
1.需要一个UI面板管理器,逻辑写在lua里面,方便热更新。
2.管理器控制面板的打开(show),隐藏(Hide),销毁(Destroy),刷新(Rest)。
3.要有类似网页浏览器那样,点击后退(<---),会显示上一个页面。用到数据结构:栈(Stack),先进后出。打开顺序是线性的,点击“后退“”会线性的回退到上一层。
5.顶部固定的“后退”按钮所在面板,在上一层没的退的情况下,会Hide。
6.面板第一次打开是Instantiate,关闭是Hide,再次打开是Rest。避免频繁的 Instantiate和Destroy。我的想法是所有面板都是在切场景的时候,才全部Destroy。回收内存。
先看结果,不知道能不能吸引你继续往下看!
XPage:
1 using System; 2 using UnityEngine; 3 using System.Collections; 4 using Object = UnityEngine.Object; 5 using LuaFramework; 6 7 public enum EPageType 8 { 9 None, 10 Normal, 11 PopUp, 12 Fixed, 13 Toppest, 14 } 15 16 public enum EPageMode 17 { 18 DoNothing, 19 HideOtherOnly, 20 HideOtherAndNeedBack, 21 } 22 23 public enum EPageState 24 { 25 NONE, 26 OPEN, //打开 27 HIDE, //隐藏 28 CLOSE,//销毁 29 } 30 31 public class XPageLoadBind 32 { 33 /// <summary> 34 /// 绑定你自己的资源加载管理器 35 /// </summary> 36 /// <param name="xPage"></param> 37 public static void Bind(XPage xPage) 38 { 39 xPage.delegateSyncLoadUI = Resources.Load; 40 //xPage.delegateAsyncLoadUI = ResourcesMgr.Load; 41 } 42 } 43 44 public class XPage 45 { 46 public string m_pageName; 47 public string m_loadPath; 48 public GameObject m_pageInst; 49 public Transform m_pageTrans; 50 public EPageType m_pageType = EPageType.None; 51 public EPageMode m_pageMode = EPageMode.DoNothing; 52 public EPageState m_currState = EPageState.NONE; 53 54 private string m_luaPageCtrl; 55 private string m_luaPageView; 56 57 //delegate load ui function. 58 public Func<string, Object> delegateSyncLoadUI = null; 59 public Action<string, Action<Object>> delegateAsyncLoadUI = null; 60 61 public XPage(string pageName, string loadPath) 62 { 63 m_pageName = pageName; 64 m_loadPath = loadPath; 65 } 66 67 public void Awake() 68 { 69 m_luaPageCtrl = m_pageName + "Ctrl"; 70 m_luaPageView = m_pageName + "View"; 71 //Debug.LogError("call lua awake :(" + m_pageName + "Ctrl)"); 72 Util.CallMethod(m_luaPageCtrl, "Awake",this); 73 74 //设置type和mode 75 //m_pageType = EPageType.PopUp; 76 //m_pageMode = EPageMode.HideOtherAndNeedBack; 77 } 78 79 public void Start() 80 { 81 m_currState = EPageState.OPEN; 82 m_pageInst.gameObject.SetActive(true); 83 AnchorUIGameObject(); 84 //Debug.LogError("call lua start :(" + m_pageName + "Ctrl)"); 85 Util.CallMethod(m_luaPageView, "Start", this.m_pageInst); 86 Util.CallMethod(m_luaPageCtrl, "Start"); 87 } 88 89 public void Rest() 90 { 91 m_currState = EPageState.OPEN; 92 m_pageInst.gameObject.SetActive(true); 93 //Debug.LogError("call lua rest :(" + m_pageName + "Ctrl)"); 94 Util.CallMethod(m_luaPageCtrl, "Rest"); 95 } 96 97 public void Hide() 98 { 99 m_currState = EPageState.HIDE; 100 m_pageInst.gameObject.SetActive(false); 101 //Debug.LogError("call lua hide :(" + m_pageName + "Ctrl)"); 102 Util.CallMethod(m_luaPageCtrl, "Hide"); 103 } 104 105 public void Destroy() 106 { 107 m_currState = EPageState.CLOSE; 108 GameObject.Destroy(m_pageInst); 109 //Debug.LogError("call lua destroy :(" + m_pageName + "Ctrl)"); 110 Util.CallMethod(m_luaPageCtrl, "Destroy"); 111 } 112 113 public void LoadSync(Action<GameObject> callback) 114 { 115 if (this.m_pageInst == null && string.IsNullOrEmpty(m_loadPath) == false) 116 { 117 GameObject go = null; 118 if (delegateSyncLoadUI != null) 119 { 120 Object o = delegateSyncLoadUI(m_loadPath); 121 go = o != null ? GameObject.Instantiate(o) as GameObject : null; 122 } 123 else 124 { 125 go = GameObject.Instantiate(Resources.Load(m_loadPath)) as GameObject; 126 } 127 128 if (go == null) 129 { 130 Debug.LogError("[UI] Cant sync load your ui prefab."); 131 return; 132 } 133 134 m_pageInst = go; 135 m_pageTrans = go.transform; 136 137 if (callback != null) 138 callback(go); 139 } 140 else 141 { 142 if (callback != null) 143 callback(m_pageInst); 144 } 145 } 146 147 public void LoadAsync(Action<GameObject> callback) 148 { 149 XPageRoot.Instance.StartCoroutine(AsyncShow(callback)); 150 } 151 152 IEnumerator AsyncShow(Action<GameObject> callback) 153 { 154 if (this.m_pageInst == null && string.IsNullOrEmpty(m_loadPath) == false) 155 { 156 GameObject go = null; 157 bool _loading = true; 158 delegateAsyncLoadUI(m_loadPath, (o) => 159 { 160 go = o != null ? GameObject.Instantiate(o) as GameObject : null; 161 162 _loading = false; 163 164 m_pageInst = go; 165 m_pageTrans = go.transform; 166 167 if (callback != null) 168 callback(go); 169 }); 170 171 float _t0 = Time.realtimeSinceStartup; 172 while (_loading) 173 { 174 if (Time.realtimeSinceStartup - _t0 >= 10.0f) 175 { 176 Debug.LogError("[UI] WTF async load your ui prefab timeout!"); 177 yield break; 178 } 179 yield return null; 180 } 181 } 182 else 183 { 184 if (callback != null) 185 callback(m_pageInst); 186 } 187 } 188 189 protected void AnchorUIGameObject() 190 { 191 if (XPageRoot.Instance == null || m_pageInst == null) 192 return; 193 194 GameObject ui = m_pageInst; 195 196 //check if this is ugui or (ngui)? 197 Vector3 anchorPos = Vector3.zero; 198 Vector2 sizeDel = Vector2.zero; 199 Vector3 scale = Vector3.one; 200 if (ui.GetComponent<RectTransform>() != null) 201 { 202 anchorPos = ui.GetComponent<RectTransform>().anchoredPosition; 203 sizeDel = ui.GetComponent<RectTransform>().sizeDelta; 204 scale = ui.GetComponent<RectTransform>().localScale; 205 } 206 else 207 { 208 anchorPos = ui.transform.localPosition; 209 scale = ui.transform.localScale; 210 } 211 212 EPageType type = this.m_pageType; 213 if (type == EPageType.Normal) 214 { 215 ui.transform.SetParent(XPageRoot.Instance.normalRoot); 216 } 217 else if(type == EPageType.PopUp) 218 { 219 ui.transform.SetParent(XPageRoot.Instance.popupRoot); 220 } 221 else if (type == EPageType.Fixed) 222 { 223 ui.transform.SetParent(XPageRoot.Instance.fixedRoot); 224 } 225 else if (type == EPageType.Toppest) 226 { 227 ui.transform.SetParent(XPageRoot.Instance.ToppestRoot); 228 } 229 230 if (ui.GetComponent<RectTransform>() != null) 231 { 232 ui.GetComponent<RectTransform>().anchoredPosition = anchorPos; 233 ui.GetComponent<RectTransform>().sizeDelta = sizeDel; 234 ui.GetComponent<RectTransform>().localScale = scale; 235 } 236 else 237 { 238 ui.transform.localPosition = anchorPos; 239 ui.transform.localScale = scale; 240 } 241 } 242 }
XpageMgr:
1 using UnityEngine; 2 using System.Collections.Generic; 3 using System; 4 5 public class XPageMgr 6 { 7 private static XPageMgr m_inst; 8 public static XPageMgr Inst 9 { 10 get 11 { 12 if (m_inst == null) 13 m_inst = new XPageMgr(); 14 return m_inst; 15 } 16 } 17 18 public XPage currShowXPage;//当前正打开的界面 19 public Stack<XPage> m_pageNeedBackPool = new Stack<XPage>();//需要返回的页面的池子,栈,先进后出 20 public Dictionary<string, XPage> m_pageDic = new Dictionary<string, XPage>();//所有的页面 21 22 23 public int GetNeedBackCount() 24 { 25 return m_pageNeedBackPool.Count; 26 } 27 28 /// <summary> 29 /// 获取所有面板 30 /// </summary> 31 /// <returns></returns> 32 private List<XPage> GetAllPages() 33 { 34 return new List<XPage>(m_pageDic.Values); 35 } 36 37 /// <summary> 38 /// 检查面板打开类型 39 /// </summary> 40 /// <param name="currXPage"></param> 41 private void CheckPageMode(XPage currXPage) 42 { 43 if (currXPage.m_pageMode == EPageMode.DoNothing) 44 { 45 46 } 47 else if (currXPage.m_pageMode == EPageMode.HideOtherOnly) 48 { 49 HideOtherPages(currXPage); 50 } 51 else if (currXPage.m_pageMode == EPageMode.HideOtherAndNeedBack) 52 { 53 HideOtherPages(currXPage); 54 m_pageNeedBackPool.Push(currXPage); 55 } 56 } 57 58 private void HideOtherPages(XPage currXPage) 59 { 60 List<XPage> xpages = GetAllPages(); 61 int count = xpages.Count; 62 for (int i = 0; i < count; i++) 63 { 64 XPage curr = xpages[i]; 65 if (curr.Equals(currXPage)) 66 continue; 67 if (curr.m_currState == EPageState.OPEN && curr.m_pageType != EPageType.Fixed && curr.m_pageType != EPageType.Normal ) 68 { 69 curr.Hide(); 70 } 71 } 72 } 73 74 /// <summary> 75 /// 检测面板是否在队列里 76 /// </summary> 77 /// <param name="pageName"></param> 78 /// <returns></returns> 79 private bool CheckPageExist(string pageName) 80 { 81 if (m_pageDic.ContainsKey(pageName)) 82 { 83 return true; 84 } 85 else 86 { 87 return false; 88 } 89 } 90 91 /// <summary> 92 /// 用相对路径获取面板名称 93 /// </summary> 94 /// <param name="pageLoadPath"></param> 95 /// <returns></returns> 96 private string GetPageName(string pageLoadPath) 97 { 98 string pageName = pageLoadPath.Substring(pageLoadPath.LastIndexOf("/") + 1); 99 return pageName; 100 } 101 102 #region api 103 /// <summary> 104 /// 打开面板 105 /// </summary> 106 /// <param name="isSync">是否同步加载</param> 107 /// <param name="pageLoadPath">加载的相对路径</param> 108 public void ShowPage(bool isSync, string pageLoadPath) 109 { 110 string pageName = GetPageName(pageLoadPath); 111 bool isExist = CheckPageExist(pageName); 112 XPage currXPage = null; 113 if (isExist) 114 { 115 currXPage = m_pageDic[pageName]; 116 if(currXPage.m_currState == EPageState.HIDE) 117 { 118 CheckPageMode(currXPage); 119 currXPage.Rest(); 120 currShowXPage = currXPage; 121 } 122 } 123 else 124 { 125 //add 126 currXPage = new XPage(pageName, pageLoadPath); 127 currXPage.Awake(); 128 XPageLoadBind.Bind(currXPage); 129 if (isSync) 130 { 131 currXPage.LoadSync((go) => 132 { 133 m_pageDic.Add(pageName, currXPage); 134 currShowXPage = currXPage; 135 CheckPageMode(currXPage); 136 currXPage.Start(); 137 138 }); 139 } 140 else 141 { 142 currXPage.LoadAsync((go) => 143 { 144 m_pageDic.Add(pageName, currXPage); 145 currShowXPage = currXPage; 146 CheckPageMode(currXPage); 147 currXPage.Start(); 148 }); 149 } 150 } 151 } 152 153 /// <summary> 154 /// 隐藏当前的页面 155 /// </summary> 156 public bool HideCurrPage() 157 { 158 if (currShowXPage != null) 159 { 160 if (currShowXPage.m_pageMode == EPageMode.HideOtherAndNeedBack) 161 { 162 if (m_pageNeedBackPool.Count > 0) 163 { 164 if (m_pageNeedBackPool.Peek().Equals(currShowXPage)) 165 { 166 XPage topPage = m_pageNeedBackPool.Pop(); 167 topPage.Hide(); 168 currShowXPage = null; 169 170 if (m_pageNeedBackPool.Count > 0) 171 { 172 XPage _curr = m_pageNeedBackPool.Peek(); 173 _curr.Rest(); 174 currShowXPage = _curr; 175 } 176 } 177 } 178 } 179 else 180 { 181 if (currShowXPage.m_currState == EPageState.OPEN) 182 { 183 currShowXPage.Hide(); 184 currShowXPage = null; 185 } 186 } 187 188 return true; 189 } 190 else 191 { 192 Debug.Log("currShowPage is null"); 193 return false; 194 } 195 } 196 197 /// <summary> 198 ///隐藏指定面板 199 /// </summary> 200 /// <param name="pageName">Page name.</param> 201 public void HidePage(string pageName) 202 { 203 bool isExist = CheckPageExist(pageName); 204 if (isExist) 205 { 206 XPage _currXpage = m_pageDic[pageName]; 207 if(_currXpage.m_currState == EPageState.OPEN) 208 _currXpage.Hide(); 209 } 210 211 } 212 213 /// <summary> 214 /// 销毁所有面板 215 /// </summary> 216 public void CloseAllPages() 217 { 218 List<XPage> allPages = GetAllPages(); 219 int count = allPages.Count; 220 for (int i = 0; i < count; i++) 221 { 222 allPages[i].Destroy(); 223 allPages[i] = null; 224 } 225 m_pageDic.Clear(); 226 m_pageNeedBackPool.Clear(); 227 } 228 #endregion 229 230 /// <summary> 231 /// 销毁 232 /// </summary> 233 public void Destroy() 234 { 235 CloseAllPages(); 236 currShowXPage = null; 237 m_pageDic = null; 238 m_pageNeedBackPool = null; 239 m_inst = null; 240 Debug.Log("~XPageMgr was destroy"); 241 } 242 }
XPageRoot:
1 using UnityEngine; 2 using System.Collections; 3 using UnityEngine.UI; 4 using UnityEngine.EventSystems; 5 6 /// <summary> 7 /// Init The UI Root 8 /// </summary> 9 public class XPageRoot : MonoBehaviour 10 { 11 private static XPageRoot m_Instance = null; 12 public static XPageRoot Instance 13 { 14 get 15 { 16 if (m_Instance == null) 17 { 18 InitRoot(); 19 } 20 return m_Instance; 21 } 22 } 23 24 public Transform root; 25 public Transform normalRoot;//Canvas order in layer 0 26 public Transform popupRoot;//250 27 public Transform fixedRoot;//500 28 public Transform ToppestRoot;//750 29 public Camera uiCamera; 30 31 static void InitRoot() 32 { 33 GameObject go = new GameObject("UIRoot"); 34 go.layer = LayerMask.NameToLayer("UI"); 35 m_Instance = go.AddComponent<XPageRoot>(); 36 go.AddComponent<RectTransform>(); 37 m_Instance.root = go.transform; 38 39 Canvas can = go.AddComponent<Canvas>(); 40 can.renderMode = RenderMode.ScreenSpaceCamera; 41 can.pixelPerfect = true; 42 GameObject camObj = new GameObject("UICamera"); 43 camObj.layer = LayerMask.NameToLayer("UI"); 44 camObj.transform.parent = go.transform; 45 camObj.transform.localPosition = new Vector3(0, 0, -100f); 46 Camera cam = camObj.AddComponent<Camera>(); 47 cam.clearFlags = CameraClearFlags.Depth; 48 cam.orthographic = true; 49 cam.farClipPlane = 200f; 50 can.worldCamera = cam; 51 m_Instance.uiCamera = cam; 52 cam.cullingMask = 1 << 5; 53 cam.nearClipPlane = -50f; 54 cam.farClipPlane = 50f; 55 56 //add audio listener 57 camObj.AddComponent<AudioListener>(); 58 camObj.AddComponent<GUILayer>(); 59 60 CanvasScaler cs = go.AddComponent<CanvasScaler>(); 61 cs.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; 62 cs.referenceResolution = new Vector2(1136f, 640f); 63 cs.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand; 64 65 ////add auto scale camera fix size. 66 //TTCameraScaler tcs = go.AddComponent<TTCameraScaler>(); 67 //tcs.scaler = cs; 68 69 //set the raycaster 70 //GraphicRaycaster gr = go.AddComponent<GraphicRaycaster>(); 71 72 GameObject subRoot = CreateSubCanvasForRoot(go.transform, 0); 73 subRoot.name = "NormalRoot"; 74 m_Instance.normalRoot = subRoot.transform; 75 76 subRoot = CreateSubCanvasForRoot(go.transform, 250); 77 subRoot.name = "PopupRoot"; 78 m_Instance.popupRoot = subRoot.transform; 79 80 subRoot = CreateSubCanvasForRoot(go.transform, 500); 81 subRoot.name = "FixedRoot"; 82 m_Instance.fixedRoot = subRoot.transform; 83 84 subRoot = CreateSubCanvasForRoot(go.transform, 750); 85 subRoot.name = "ToppestRoot"; 86 m_Instance.ToppestRoot = subRoot.transform; 87 88 //add Event System 89 GameObject esObj = GameObject.Find("EventSystem"); 90 if (esObj != null) 91 { 92 GameObject.DestroyImmediate(esObj); 93 } 94 95 GameObject eventObj = new GameObject("EventSystem"); 96 eventObj.layer = LayerMask.NameToLayer("UI"); 97 eventObj.transform.SetParent(go.transform); 98 eventObj.AddComponent<EventSystem>(); 99 if (!Application.isMobilePlatform || Application.isEditor) 100 { 101 eventObj.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>(); 102 } 103 else 104 { 105 eventObj.AddComponent<UnityEngine.EventSystems.TouchInputModule>(); 106 } 107 108 109 } 110 111 static GameObject CreateSubCanvasForRoot(Transform root, int sort) 112 { 113 GameObject go = new GameObject("canvas"); 114 go.transform.parent = root; 115 go.layer = LayerMask.NameToLayer("UI"); 116 117 Canvas can = go.AddComponent<Canvas>(); 118 RectTransform rect = go.GetComponent<RectTransform>(); 119 rect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, 0, 0); 120 rect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, 0); 121 rect.anchorMin = Vector2.zero; 122 rect.anchorMax = Vector2.one; 123 124 can.overrideSorting = true; 125 can.sortingOrder = sort; 126 127 go.AddComponent<GraphicRaycaster>(); 128 129 return go; 130 } 131 132 void OnDestroy() 133 { 134 m_Instance = null; 135 } 136 }
ToLua使用:
熟悉Tolua的同学都知道要
_GT(typeof(XPage)),
_GT(typeof(XPageMgr)),
_GT(typeof(EPageType)),
_GT(typeof(EPageMode)),
_GT(typeof(EventTriggerListener)),
如果你还不知道ToLua是什么,那么你先去了解一下咯。
这样,这些类,在lua里就能用了。
初始化:
1 require "Logic/LuaClass" 2 require "Common/define" 3 require "Common/functions" 4 5 --管理器-- 6 XGame = {}; 7 local this = XGame; 8 9 local game; 10 local transform; 11 local gameObject; 12 local WWW = UnityEngine.WWW; 13 14 function XGame.InitViewPanels() 15 for i = 1, #XPanelNames do 16 require ("XUI/XView/"..tostring(XPanelNames[i]).."View") 17 require ("XUI/XCtrl/"..tostring(XPanelNames[i]).."Ctrl") 18 end 19 end 20 21 xpageMgr = XPageMgr.Inst 22 23 --初始化完成,发送链接服务器信息-- 24 function XGame.OnInitOK() 25 --注册LuaView-- 26 this.InitViewPanels(); 27 28 29 logWarn('LuaFramework InitOK--->>>'); 30 end
当你调XPageMgr.Inst.ShowPage(true, "UI/UIPrefab/MainPanel");来显示MainPanel的面板时
我的做法是去调lua里面MainPanelCtrl,MainPanelView。
MainPanelCtrl
1 local transform; 2 local gameObject; 3 4 MainPanelCtrl = {}; 5 local this = MainPanelCtrl; 6 7 --构建函数-- 8 function MainPanelCtrl.New() 9 logWarn("MainPanelCtrl.New--->>"); 10 return this; 11 end 12 13 function MainPanelCtrl.Awake(xpage) 14 --logWarn('MainPanelCtrl Awake--->>>'..'xpage name:'..xpage.m_pageName); 15 xpage.m_pageType = EPageType.Normal; 16 xpage.m_pageMode = EPageMode.DoNothing; 17 end 18 19 function MainPanelCtrl.Start() 20 logWarn('MainPanelCtrl Start--->>>'); 21 local eventTriggerListener = EventTriggerListener.Get(MainPanelView.packageBtn.gameObject); 22 eventTriggerListener:AddClick(MainPanelView.packageBtn,this.OnClick); 23 end 24 25 function MainPanelCtrl.Rest() 26 logWarn('MainPanelCtrl Rest--->>>'); 27 end 28 29 function MainPanelCtrl.Hide() 30 logWarn('MainPanelCtrl Hide--->>>'); 31 end 32 33 function MainPanelCtrl.Destroy() 34 logWarn('MainPanelCtrl Destroy--->>>'); 35 end 36 37 --单击事件-- 38 function MainPanelCtrl.OnClick(go) 39 xpageMgr:ShowPage(true,"UI/UIPrefab/TopBar"); 40 xpageMgr:ShowPage(true,"UI/Prompt/PromptPanel"); 41 end
MainPanelView
1 local transform; 2 local gameObject; 3 4 MainPanelView = {}; 5 local this = MainPanelView; 6 7 8 function MainPanelView.Start(obj) 9 gameObject = obj; 10 transform = obj.transform; 11 logWarn('MainPanelView Start--->>>'..gameObject.name); 12 13 this.packageBtn = transform:FindChild("Button").gameObject; 14 end
其他面板也是这个道理,所以我的结构是这样的。
结束语:
1.代码写的很粗糙,但是基本满足我目前的需求。
2.再次感谢开源,避免重复造轮子,毕竟人生苦短,当你有想法时,也许可以进行二次开发。