首先是资源(我这里是UI,一个Panel为一个资源),对应着一个MonoBehaviour的脚本和一个功能一致的Lua脚本,将资源打包成AssetBundle,lua脚本不需要打包(加密应该是必须的),都放到远程服务器上。通过配置表Table(也是远程加载的)决定是使用MonoBehavoiur还是Lua脚本,使用一个UILuaBehaviour来承载保存lua的模块名,UILuaBehaviour是一些UI常用到的方法,从而在Lua脚本中可以获取这个脚本调用这些功能。
启动的时候,在GameLuaManager中初始化时实例化LuaState或LuaScriptMgr,调用Start方法开始,调用一个Lua脚本的管理器加载一些公用的依赖,以便后面lua脚本使用这些公用方法。
using UnityEngine; using System.Collections; using System.IO; using System; public class GameLuaManager : Singleton<GameLuaManager> { LuaScriptMgr luaScript = null; public void Initialize() { if (luaScript == null) { luaScript = new LuaScriptMgr(); luaScript.Start(); luaScript.DoFile("Logic/GameManager"); Util.CallMethod("GameManager", "Main"); } } public LuaScriptMgr LuaManager { get{return luaScript;} } }
--Lua GameManager.lua require "Common/define" --在这里就引入全局了,一些全局定义 require "Net/Network" --网络通信 require "Common/functions" --一些工具的方法 --管理器-- GameManager = {}; local this = GameManager; function GameManager.Main() require("Net/Network").new() end --例如functions定义了一些打印日志的方法,Util是c#脚本warp映射到lua中的 --输出日志-- function log(str) Util.Log(str); end --错误日志-- function error(str) Util.LogError(str); end --警告日志-- function warn(str) Util.LogWarning(str); end
然后各个管理器初始化完成,检查资源更新,会把需要的UI面板和对应的Lua脚本下载到本地上,GameUIManager会根据需求加载UI面板并通过配置查看是否使用lua从而添加UILuaBehaviour,UILuaBehaviour会根据面板的名字找到Lua模块,然后Awake,Start等方法触发是直接Call的Lua中的方法。
//GameUIManager public PanelBase Show(string strName, bool useCoroutine = true, bool fromPakLoad = false, bool bNeedDarkFollow = false) { UIAsset asset = GetUIAsset(strName); if (asset == null) return null; //控制主界面隐藏 if (asset.ui.IsCloseMain) { GetUIAsset("Main").panel.gameObject.SetActive(false); } if (asset.ui.IsHideChar) { CharMagr.Get().GetLoader().IsVisible = false; } if (Main.gameState == Game_State_Type.GST_Fight) { if (asset.ui.fightShow == 0) return null; } HideAll(asset); if (asset.ui.enableDark) ShowDark(true , false ,asset); uiTimeList.Remove(asset); Main.Game.IsHoverUI = false; if (asset.oUIObject != null) { asset.oUIObject.SetActive(true); if (asset.panel != null) { asset.panel.OnShow(); } else if (asset.luaBehaviour != null) { asset.luaBehaviour.OnShow(); } uiActive.Add(asset); return asset.panel; } //主要在这里,首次加载的 UnityEngine.Object obj = null; if (asset.ui.lua == "") { // cs的界面 obj = CorResMgr.Get().LoadResource(CONST_VALUE.UI_ROOT_PATH + asset.ui.prefab); if (obj == null) return null; asset.oUIObject = (GameObject)GameObject.Instantiate(obj); asset.oUIObject.transform.parent = root.transform; asset.oUIObject.transform.localScale = Vector3.one * CalcScale(asset); asset.oUIObject.transform.localPosition = Vector3.zero; asset.panel = asset.oUIObject.GetComponent<PanelBase>(); } else { // lua的界面 obj = CorResMgr.Get().LoadResource(CONST_VALUE.UI_ROOT_PATH + asset.ui.prefab); if (obj == null) return null; asset.oUIObject = (GameObject)GameObject.Instantiate(obj); asset.oUIObject.transform.parent = root.transform; asset.oUIObject.transform.localScale = Vector3.one * CalcScale(asset); asset.oUIObject.transform.localPosition = Vector3.zero; GameLuaManager.Get().LuaManager.DoFile("UI/" + asset.ui.lua);//编译配置表指定Lua脚本 //添加UILuaBehaviour脚本来做中间人去驱动调用lua方法 asset.luaBehaviour = asset.oUIObject.AddComponent<UILuaBehaviour>(); } UIPanel n_ObjPanel = asset.oUIObject.GetComponent<UIPanel>(); if (n_ObjPanel) n_ObjPanel.depth = asset.ui.Depth; UIAnchor[] anchors = asset.oUIObject.GetComponentsInChildren<UIAnchor>(); for (int i = 0; i < anchors.Length; i++) { anchors[i].uiCamera = uiCamera; } if (asset.panel != null) { asset.panel.OnShow(); } else if (asset.luaBehaviour != null) { asset.luaBehaviour.OnShow(); } uiActive.Add(asset); if (asset.panel != null) { asset.panel.m_UIName = strName; } obj = null; return asset.panel; }
下面看一下UILuaBehaviour
public class UILuaBehaviour : PanelBase { protected static bool initialize = false; private string data = null; private List<LuaFunction> buttons = new List<LuaFunction>(); private string luaFileName = ""; protected void Awake() { luaFileName = name.Split('_')[1]; //这里自动就知道模块lua的文件名了 CallMethod("Awake", gameObject);//把gameobject传递给lua知道,然它知道控制的是谁 } void Start() { if (initialize) return; CallMethod("Start"); initialize = true; } public override void OnShow() { CallMethod("OnShow"); } public virtual void OnHide() { CallMethod("OnHide"); } public override void ValueChange(UICmd cmd) { CallMethod("ValueChange", cmd.key, cmd.values); } /// <summary> /// 添加单击事件 /// </summary> public void AddClick(string ctrName, LuaFunction luafunc) { if (string.IsNullOrEmpty(ctrName)) return; GameObject go = transform.FindChild(ctrName).gameObject; if (go == null) return; UIEventListener.Get(go).onClick = delegate (GameObject o) { luafunc.Call(go); buttons.Add(luafunc); }; } /// <summary> /// 根据gameobject绑定按钮事件 /// </summary> /// <param name="btn"></param> /// <param name="luafunc"></param> public void AddClickForObj(GameObject btn, LuaFunction luafunc) { if (btn == null) return; UIEventListener.Get(btn).onClick = delegate (GameObject o) { luafunc.Call(btn); buttons.Add(luafunc); }; } /// <summary> /// 带一个整形参数的回调 /// </summary> public void AddClickForObjWithIntParam(GameObject btn, LuaFunction luafunc, int iParam) { if (btn == null) return; UIEventListener.Get(btn).onClick = delegate (GameObject o) { luafunc.Call(iParam); buttons.Add(luafunc); }; } /// <summary> /// 清除单击事件 /// </summary> public void ClearClick() { for (int i = 0; i < buttons.Count; i++) { if (buttons[i] != null) { buttons[i].Dispose(); buttons[i] = null; } } } /// <summary> /// 执行Lua方法 /// </summary> protected object[] CallMethod(string func, params object[] args) { return Util.CallMethod(luaFileName, func, args); } public override void OnDestroy() { ClearClick(); initialize = false; CallMethod("OnDestroy"); } }
最后看一下上面最常用到的Util.CallMethod方法,工具方法调用lua的方法
/// <summary> /// 执行Lua方法 /// </summary> public static object[] CallMethod(string module, string func, params object[] args) { LuaScriptMgr luaMgr = GameLuaManager.Get().LuaManager; if (luaMgr == null) return null; string funcName = module + "." + func; funcName = funcName.Replace("(Clone)", ""); return luaMgr.CallLuaFunction(funcName, args); }
最后看一个ui的lua脚本例子
--LoginZiJia.lua LoginZiJia = {} --这个模块,对应LoginZiJia这个UIPrefab local this = LoginZiJia local gameObject = nil local playerName = "" local playerSex = 0 local playerProfession = 0 local iP = "" local port = 0 function LoginZiJia.Awake(obj) gameObject = obj --把实例化的GameObject传递过来了 end function LoginZiJia.Start() playerName = GameObject.Find('Scale/Input_Name'):GetComponent('UIInput') playerName.text = "test2018" playerSex = GameObject.Find('Scale/Input_Sex'):GetComponent('UIInput') playerSex.text ='0' playerProfession = GameObject.Find('Scale/Input_Profession'):GetComponent('UIInput') playerProfession.text = '0' iP = GameObject.Find('Scale/Input_IP'):GetComponent('UIInput') iP.text = GameAPI.getZiJiaIP() port = GameObject.Find('Scale/Input_Port'):GetComponent('UIInput') port.text = tostring(GameAPI.getZiJiaPort()) --获取UILuaBehaviour可以使用一些方便的方法 local uilua = gameObject:GetComponent('UILuaBehaviour') uilua:AddClick('Scale/LoginBtn',this.onLogin) uilua:AddClick('Scale/LoginReturn',this.onBack) end