首先是资源(我这里是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