Unity3d:UI面板管理整合进ToLua

时间:2023-03-09 00:20:33
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。回收内存。

先看结果,不知道能不能吸引你继续往下看!

Unity3d:UI面板管理整合进ToLua

XPage:

 using System;
using UnityEngine;
using System.Collections;
using Object = UnityEngine.Object;
using LuaFramework; public enum EPageType
{
None,
Normal,
PopUp,
Fixed,
Toppest,
} public enum EPageMode
{
DoNothing,
HideOtherOnly,
HideOtherAndNeedBack,
} public enum EPageState
{
NONE,
OPEN, //打开
HIDE, //隐藏
CLOSE,//销毁
} public class XPageLoadBind
{
/// <summary>
/// 绑定你自己的资源加载管理器
/// </summary>
/// <param name="xPage"></param>
public static void Bind(XPage xPage)
{
xPage.delegateSyncLoadUI = Resources.Load;
//xPage.delegateAsyncLoadUI = ResourcesMgr.Load;
}
} public class XPage
{
public string m_pageName;
public string m_loadPath;
public GameObject m_pageInst;
public Transform m_pageTrans;
public EPageType m_pageType = EPageType.None;
public EPageMode m_pageMode = EPageMode.DoNothing;
public EPageState m_currState = EPageState.NONE; private string m_luaPageCtrl;
private string m_luaPageView; //delegate load ui function.
public Func<string, Object> delegateSyncLoadUI = null;
public Action<string, Action<Object>> delegateAsyncLoadUI = null; public XPage(string pageName, string loadPath)
{
m_pageName = pageName;
m_loadPath = loadPath;
} public void Awake()
{
m_luaPageCtrl = m_pageName + "Ctrl";
m_luaPageView = m_pageName + "View";
//Debug.LogError("call lua awake :(" + m_pageName + "Ctrl)");
Util.CallMethod(m_luaPageCtrl, "Awake",this); //设置type和mode
//m_pageType = EPageType.PopUp;
//m_pageMode = EPageMode.HideOtherAndNeedBack;
} public void Start()
{
m_currState = EPageState.OPEN;
m_pageInst.gameObject.SetActive(true);
AnchorUIGameObject();
//Debug.LogError("call lua start :(" + m_pageName + "Ctrl)");
Util.CallMethod(m_luaPageView, "Start", this.m_pageInst);
Util.CallMethod(m_luaPageCtrl, "Start");
} public void Rest()
{
m_currState = EPageState.OPEN;
m_pageInst.gameObject.SetActive(true);
//Debug.LogError("call lua rest :(" + m_pageName + "Ctrl)");
Util.CallMethod(m_luaPageCtrl, "Rest");
} public void Hide()
{
m_currState = EPageState.HIDE;
m_pageInst.gameObject.SetActive(false);
//Debug.LogError("call lua hide :(" + m_pageName + "Ctrl)");
Util.CallMethod(m_luaPageCtrl, "Hide");
} public void Destroy()
{
m_currState = EPageState.CLOSE;
GameObject.Destroy(m_pageInst);
//Debug.LogError("call lua destroy :(" + m_pageName + "Ctrl)");
Util.CallMethod(m_luaPageCtrl, "Destroy");
} public void LoadSync(Action<GameObject> callback)
{
if (this.m_pageInst == null && string.IsNullOrEmpty(m_loadPath) == false)
{
GameObject go = null;
if (delegateSyncLoadUI != null)
{
Object o = delegateSyncLoadUI(m_loadPath);
go = o != null ? GameObject.Instantiate(o) as GameObject : null;
}
else
{
go = GameObject.Instantiate(Resources.Load(m_loadPath)) as GameObject;
} if (go == null)
{
Debug.LogError("[UI] Cant sync load your ui prefab.");
return;
} m_pageInst = go;
m_pageTrans = go.transform; if (callback != null)
callback(go);
}
else
{
if (callback != null)
callback(m_pageInst);
}
} public void LoadAsync(Action<GameObject> callback)
{
XPageRoot.Instance.StartCoroutine(AsyncShow(callback));
} IEnumerator AsyncShow(Action<GameObject> callback)
{
if (this.m_pageInst == null && string.IsNullOrEmpty(m_loadPath) == false)
{
GameObject go = null;
bool _loading = true;
delegateAsyncLoadUI(m_loadPath, (o) =>
{
go = o != null ? GameObject.Instantiate(o) as GameObject : null; _loading = false; m_pageInst = go;
m_pageTrans = go.transform; if (callback != null)
callback(go);
}); float _t0 = Time.realtimeSinceStartup;
while (_loading)
{
if (Time.realtimeSinceStartup - _t0 >= 10.0f)
{
Debug.LogError("[UI] WTF async load your ui prefab timeout!");
yield break;
}
yield return null;
}
}
else
{
if (callback != null)
callback(m_pageInst);
}
} protected void AnchorUIGameObject()
{
if (XPageRoot.Instance == null || m_pageInst == null)
return; GameObject ui = m_pageInst; //check if this is ugui or (ngui)?
Vector3 anchorPos = Vector3.zero;
Vector2 sizeDel = Vector2.zero;
Vector3 scale = Vector3.one;
if (ui.GetComponent<RectTransform>() != null)
{
anchorPos = ui.GetComponent<RectTransform>().anchoredPosition;
sizeDel = ui.GetComponent<RectTransform>().sizeDelta;
scale = ui.GetComponent<RectTransform>().localScale;
}
else
{
anchorPos = ui.transform.localPosition;
scale = ui.transform.localScale;
} EPageType type = this.m_pageType;
if (type == EPageType.Normal)
{
ui.transform.SetParent(XPageRoot.Instance.normalRoot);
}
else if(type == EPageType.PopUp)
{
ui.transform.SetParent(XPageRoot.Instance.popupRoot);
}
else if (type == EPageType.Fixed)
{
ui.transform.SetParent(XPageRoot.Instance.fixedRoot);
}
else if (type == EPageType.Toppest)
{
ui.transform.SetParent(XPageRoot.Instance.ToppestRoot);
} if (ui.GetComponent<RectTransform>() != null)
{
ui.GetComponent<RectTransform>().anchoredPosition = anchorPos;
ui.GetComponent<RectTransform>().sizeDelta = sizeDel;
ui.GetComponent<RectTransform>().localScale = scale;
}
else
{
ui.transform.localPosition = anchorPos;
ui.transform.localScale = scale;
}
}
}

XpageMgr:

 using UnityEngine;
using System.Collections.Generic;
using System; public class XPageMgr
{
private static XPageMgr m_inst;
public static XPageMgr Inst
{
get
{
if (m_inst == null)
m_inst = new XPageMgr();
return m_inst;
}
} public XPage currShowXPage;//当前正打开的界面
public Stack<XPage> m_pageNeedBackPool = new Stack<XPage>();//需要返回的页面的池子,栈,先进后出
public Dictionary<string, XPage> m_pageDic = new Dictionary<string, XPage>();//所有的页面 public int GetNeedBackCount()
{
return m_pageNeedBackPool.Count;
} /// <summary>
/// 获取所有面板
/// </summary>
/// <returns></returns>
private List<XPage> GetAllPages()
{
return new List<XPage>(m_pageDic.Values);
} /// <summary>
/// 检查面板打开类型
/// </summary>
/// <param name="currXPage"></param>
private void CheckPageMode(XPage currXPage)
{
if (currXPage.m_pageMode == EPageMode.DoNothing)
{ }
else if (currXPage.m_pageMode == EPageMode.HideOtherOnly)
{
HideOtherPages(currXPage);
}
else if (currXPage.m_pageMode == EPageMode.HideOtherAndNeedBack)
{
HideOtherPages(currXPage);
m_pageNeedBackPool.Push(currXPage);
}
} private void HideOtherPages(XPage currXPage)
{
List<XPage> xpages = GetAllPages();
int count = xpages.Count;
for (int i = ; i < count; i++)
{
XPage curr = xpages[i];
if (curr.Equals(currXPage))
continue;
if (curr.m_currState == EPageState.OPEN && curr.m_pageType != EPageType.Fixed && curr.m_pageType != EPageType.Normal )
{
curr.Hide();
}
}
} /// <summary>
/// 检测面板是否在队列里
/// </summary>
/// <param name="pageName"></param>
/// <returns></returns>
private bool CheckPageExist(string pageName)
{
if (m_pageDic.ContainsKey(pageName))
{
return true;
}
else
{
return false;
}
} /// <summary>
/// 用相对路径获取面板名称
/// </summary>
/// <param name="pageLoadPath"></param>
/// <returns></returns>
private string GetPageName(string pageLoadPath)
{
string pageName = pageLoadPath.Substring(pageLoadPath.LastIndexOf("/") + );
return pageName;
} #region api
/// <summary>
/// 打开面板
/// </summary>
/// <param name="isSync">是否同步加载</param>
/// <param name="pageLoadPath">加载的相对路径</param>
public void ShowPage(bool isSync, string pageLoadPath)
{
string pageName = GetPageName(pageLoadPath);
bool isExist = CheckPageExist(pageName);
XPage currXPage = null;
if (isExist)
{
currXPage = m_pageDic[pageName];
if(currXPage.m_currState == EPageState.HIDE)
{
CheckPageMode(currXPage);
currXPage.Rest();
currShowXPage = currXPage;
}
}
else
{
//add
currXPage = new XPage(pageName, pageLoadPath);
currXPage.Awake();
XPageLoadBind.Bind(currXPage);
if (isSync)
{
currXPage.LoadSync((go) =>
{
m_pageDic.Add(pageName, currXPage);
currShowXPage = currXPage;
CheckPageMode(currXPage);
currXPage.Start(); });
}
else
{
currXPage.LoadAsync((go) =>
{
m_pageDic.Add(pageName, currXPage);
currShowXPage = currXPage;
CheckPageMode(currXPage);
currXPage.Start();
});
}
}
} /// <summary>
/// 隐藏当前的页面
/// </summary>
public bool HideCurrPage()
{
if (currShowXPage != null)
{
if (currShowXPage.m_pageMode == EPageMode.HideOtherAndNeedBack)
{
if (m_pageNeedBackPool.Count > )
{
if (m_pageNeedBackPool.Peek().Equals(currShowXPage))
{
XPage topPage = m_pageNeedBackPool.Pop();
topPage.Hide();
currShowXPage = null; if (m_pageNeedBackPool.Count > )
{
XPage _curr = m_pageNeedBackPool.Peek();
_curr.Rest();
currShowXPage = _curr;
}
}
}
}
else
{
if (currShowXPage.m_currState == EPageState.OPEN)
{
currShowXPage.Hide();
currShowXPage = null;
}
} return true;
}
else
{
Debug.Log("currShowPage is null");
return false;
}
} /// <summary>
///隐藏指定面板
/// </summary>
/// <param name="pageName">Page name.</param>
public void HidePage(string pageName)
{
bool isExist = CheckPageExist(pageName);
if (isExist)
{
XPage _currXpage = m_pageDic[pageName];
if(_currXpage.m_currState == EPageState.OPEN)
_currXpage.Hide();
} } /// <summary>
/// 销毁所有面板
/// </summary>
public void CloseAllPages()
{
List<XPage> allPages = GetAllPages();
int count = allPages.Count;
for (int i = ; i < count; i++)
{
allPages[i].Destroy();
allPages[i] = null;
}
m_pageDic.Clear();
m_pageNeedBackPool.Clear();
}
#endregion /// <summary>
/// 销毁
/// </summary>
public void Destroy()
{
CloseAllPages();
currShowXPage = null;
m_pageDic = null;
m_pageNeedBackPool = null;
m_inst = null;
Debug.Log("~XPageMgr was destroy");
}
}

XPageRoot:

 using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems; /// <summary>
/// Init The UI Root
/// </summary>
public class XPageRoot : MonoBehaviour
{
private static XPageRoot m_Instance = null;
public static XPageRoot Instance
{
get
{
if (m_Instance == null)
{
InitRoot();
}
return m_Instance;
}
} public Transform root;
public Transform normalRoot;//Canvas order in layer 0
public Transform popupRoot;//
public Transform fixedRoot;//
public Transform ToppestRoot;//
public Camera uiCamera; static void InitRoot()
{
GameObject go = new GameObject("UIRoot");
go.layer = LayerMask.NameToLayer("UI");
m_Instance = go.AddComponent<XPageRoot>();
go.AddComponent<RectTransform>();
m_Instance.root = go.transform; Canvas can = go.AddComponent<Canvas>();
can.renderMode = RenderMode.ScreenSpaceCamera;
can.pixelPerfect = true;
GameObject camObj = new GameObject("UICamera");
camObj.layer = LayerMask.NameToLayer("UI");
camObj.transform.parent = go.transform;
camObj.transform.localPosition = new Vector3(, , -100f);
Camera cam = camObj.AddComponent<Camera>();
cam.clearFlags = CameraClearFlags.Depth;
cam.orthographic = true;
cam.farClipPlane = 200f;
can.worldCamera = cam;
m_Instance.uiCamera = cam;
cam.cullingMask = << ;
cam.nearClipPlane = -50f;
cam.farClipPlane = 50f; //add audio listener
camObj.AddComponent<AudioListener>();
camObj.AddComponent<GUILayer>(); CanvasScaler cs = go.AddComponent<CanvasScaler>();
cs.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
cs.referenceResolution = new Vector2(1136f, 640f);
cs.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand; ////add auto scale camera fix size.
//TTCameraScaler tcs = go.AddComponent<TTCameraScaler>();
//tcs.scaler = cs; //set the raycaster
//GraphicRaycaster gr = go.AddComponent<GraphicRaycaster>(); GameObject subRoot = CreateSubCanvasForRoot(go.transform, );
subRoot.name = "NormalRoot";
m_Instance.normalRoot = subRoot.transform; subRoot = CreateSubCanvasForRoot(go.transform, );
subRoot.name = "PopupRoot";
m_Instance.popupRoot = subRoot.transform; subRoot = CreateSubCanvasForRoot(go.transform, );
subRoot.name = "FixedRoot";
m_Instance.fixedRoot = subRoot.transform; subRoot = CreateSubCanvasForRoot(go.transform, );
subRoot.name = "ToppestRoot";
m_Instance.ToppestRoot = subRoot.transform; //add Event System
GameObject esObj = GameObject.Find("EventSystem");
if (esObj != null)
{
GameObject.DestroyImmediate(esObj);
} GameObject eventObj = new GameObject("EventSystem");
eventObj.layer = LayerMask.NameToLayer("UI");
eventObj.transform.SetParent(go.transform);
eventObj.AddComponent<EventSystem>();
if (!Application.isMobilePlatform || Application.isEditor)
{
eventObj.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>();
}
else
{
eventObj.AddComponent<UnityEngine.EventSystems.TouchInputModule>();
} } static GameObject CreateSubCanvasForRoot(Transform root, int sort)
{
GameObject go = new GameObject("canvas");
go.transform.parent = root;
go.layer = LayerMask.NameToLayer("UI"); Canvas can = go.AddComponent<Canvas>();
RectTransform rect = go.GetComponent<RectTransform>();
rect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, , );
rect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, , );
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one; can.overrideSorting = true;
can.sortingOrder = sort; go.AddComponent<GraphicRaycaster>(); return go;
} void OnDestroy()
{
m_Instance = null;
}
}

ToLua使用:

熟悉Tolua的同学都知道要

_GT(typeof(XPage)),
_GT(typeof(XPageMgr)),
_GT(typeof(EPageType)),
_GT(typeof(EPageMode)),
_GT(typeof(EventTriggerListener)),

如果你还不知道ToLua是什么,那么你先去了解一下咯。

这样,这些类,在lua里就能用了。

初始化:

 require "Logic/LuaClass"
require "Common/define"
require "Common/functions" --管理器--
XGame = {};
local this = XGame; local game;
local transform;
local gameObject;
local WWW = UnityEngine.WWW; function XGame.InitViewPanels()
for i = , #XPanelNames do
require ("XUI/XView/"..tostring(XPanelNames[i]).."View")
require ("XUI/XCtrl/"..tostring(XPanelNames[i]).."Ctrl")
end
end xpageMgr = XPageMgr.Inst --初始化完成,发送链接服务器信息--
function XGame.OnInitOK()
--注册LuaView--
this.InitViewPanels(); logWarn('LuaFramework InitOK--->>>');
end

当你调XPageMgr.Inst.ShowPage(true, "UI/UIPrefab/MainPanel");来显示MainPanel的面板时

我的做法是去调lua里面MainPanelCtrl,MainPanelView。

MainPanelCtrl

 local transform;
local gameObject; MainPanelCtrl = {};
local this = MainPanelCtrl; --构建函数--
function MainPanelCtrl.New()
logWarn("MainPanelCtrl.New--->>");
return this;
end function MainPanelCtrl.Awake(xpage)
--logWarn('MainPanelCtrl Awake--->>>'..'xpage name:'..xpage.m_pageName);
xpage.m_pageType = EPageType.Normal;
xpage.m_pageMode = EPageMode.DoNothing;
end function MainPanelCtrl.Start()
logWarn('MainPanelCtrl Start--->>>');
local eventTriggerListener = EventTriggerListener.Get(MainPanelView.packageBtn.gameObject);
eventTriggerListener:AddClick(MainPanelView.packageBtn,this.OnClick);
end function MainPanelCtrl.Rest()
logWarn('MainPanelCtrl Rest--->>>');
end function MainPanelCtrl.Hide()
logWarn('MainPanelCtrl Hide--->>>');
end function MainPanelCtrl.Destroy()
logWarn('MainPanelCtrl Destroy--->>>');
end --单击事件--
function MainPanelCtrl.OnClick(go)
xpageMgr:ShowPage(true,"UI/UIPrefab/TopBar");
xpageMgr:ShowPage(true,"UI/Prompt/PromptPanel");
end

MainPanelView

 local transform;
local gameObject; MainPanelView = {};
local this = MainPanelView; function MainPanelView.Start(obj)
gameObject = obj;
transform = obj.transform;
logWarn('MainPanelView Start--->>>'..gameObject.name); this.packageBtn = transform:FindChild("Button").gameObject;
end

其他面板也是这个道理,所以我的结构是这样的。

Unity3d:UI面板管理整合进ToLua

结束语:

1.代码写的很粗糙,但是基本满足我目前的需求。

2.再次感谢开源,避免重复造*,毕竟人生苦短,当你有想法时,也许可以进行二次开发。