Unity使用tolua框架教程: LuaFramewrk

文章目录

      • 0x00 tolua下载
      • 0x01 运行Demo
      • 0x02 开发环境IDE
      • 0x03 接口讲解
          • (1) StartUp启动框架
          • (2) LuaManager核心管理器
          • (3) AppConst常量定义
          • (4) Lua代码的读取
          • (5) GameManager游戏管理器
          • (6) C#中如何直接调用lua的某个方法
          • (7) lua中如何调用C#的方法
          • (8) lua中如何使用协程
          • (9) lua解析json
          • (10) lua调用C#的托管
          • (11) lua通过反射调用C#
          • (12) nil和null

0x00 tolua下载

tolua的github下载地址:https://github.com/topameng/tolua

Unity使用tolua框架教程: LuaFramewrk_第1张图片
假设我们下载的是LuaFramework_UGUI,它是基于Unity 5.0 + UGUI + tolua构建的工程
Unity使用tolua框架教程: LuaFramewrk_第2张图片
下载下来得到一个LuaFramework_UGUI-master.zip
在这里插入图片描述

0x01 运行Demo

解压之后就是一个Unity的工程,直接用Unity打开,首次打开工程会询问生成注册文件,点击确定即可
Unity使用tolua框架教程: LuaFramewrk_第3张图片
首先要执行lua资源的生成(打AssetBundle),点击菜单【LuaFramework】-【Build Windows Resource】
会把lua代码达成AssetBundle放在StreamingAssets中。

如果你用的不是Unity5.x,而是Unity2018,那么可能会报错:
Unity使用tolua框架教程: LuaFramewrk_第4张图片
这是因为新版本的Unity有些属性和接口已经废弃了的原因,我们需要特殊处理一下
一个是Light类,一个是QualitySettings类,这两个类我们一般不需要在lua中使用,所以我们不对他们生产Wrap即可:

  1. 打开CustomSettings.cs,把 _GT(typeof(Light)), 和 _GT(typeof(QualitySettings)), 这两行注释掉
  2. 然后单击菜单【Lua】-【Clear wrap files】清理掉Wrap
  3. 然后再单击菜单【Lua】-【Generate All】重新生成Wrap,
  4. 然后再重新点击菜单【LuaFramework】-【Build Windows Resource】生成lua资源。

生成成功后,我们可以在StreamingAssets中看到很多AssetBundle文件
Unity使用tolua框架教程: LuaFramewrk_第5张图片
接下来,我们就可以运行Demo场景了。打开main场景
Unity使用tolua框架教程: LuaFramewrk_第6张图片
运行
Unity使用tolua框架教程: LuaFramewrk_第7张图片

0x02 开发环境IDE

可以使用subline,也可以使用visual studio,个人偏好使用visual studio,配合插件BabeLua

Unity写lua代码的vs插件:BabeLua: https://blog.csdn.net/linxinfa/article/details/88191485

0x03 接口讲解

(1) StartUp启动框架
AppFacade.Instance.StartUp();   //启动游戏

这个接口会抛出一个NotiConst.START_UP事件,对应的响应类是StartUpCommand

using UnityEngine;
using System.Collections;
using LuaFramework;

public class StartUpCommand : ControllerCommand {

    public override void Execute(IMessage message) {
        if (!Util.CheckEnvironment()) return;

        GameObject gameMgr = GameObject.Find("GlobalGenerator");
        if (gameMgr != null) {
            AppView appView = gameMgr.AddComponent();
        }
        //-----------------关联命令-----------------------
        AppFacade.Instance.RegisterCommand(NotiConst.DISPATCH_MESSAGE, typeof(SocketCommand));

        //-----------------初始化管理器-----------------------
        AppFacade.Instance.AddManager(ManagerName.Lua);
        AppFacade.Instance.AddManager(ManagerName.Panel);
        AppFacade.Instance.AddManager(ManagerName.Sound);
        AppFacade.Instance.AddManager(ManagerName.Timer);
        AppFacade.Instance.AddManager(ManagerName.Network);
        AppFacade.Instance.AddManager(ManagerName.Resource);
        AppFacade.Instance.AddManager(ManagerName.Thread);
        AppFacade.Instance.AddManager(ManagerName.ObjectPool);
        AppFacade.Instance.AddManager(ManagerName.Game);
    }
}

这里初始化了各种管理器,我们可以根据具体需求进行改造和自定义。

(2) LuaManager核心管理器

LuaManager这个管理器是必须的,掌管整个lua虚拟机的生命周期。它主要是加载lua库,加载lua脚本,启动lua虚拟机,执行Main.lua。

(3) AppConst常量定义

AppConst定义了一些常量。
其中AppConst.LuaBundleMode是lua代码AssetBundle模式。它会被赋值给LuaLoader的beZip变量,在加载lua代码的时候,会根据beZip的值去读取lua文件,false则去search path中读取lua文件,否则从外部设置过来的bundle文件中读取lua文件。默认为true。在Editor环境下,建议把AppConst.LuaBundleMode设为false,这样方便运行,否则写完lua代码需要生成AssetBundle才可以运行到。

(4) Lua代码的读取

LuaLoaderLuaResLoader都继承LuaFileUtils。lua代码会先从LuaFramework.Util.AppContentPath目录解压到LuaFramework.Util.DataPath目录中,lua文件列表信息记录在files.txt中,此文件也会拷贝过去。然后从LuaFramework.Util.DataPath目录中读取lua代码。

游戏工程资源目录 本地数据目录 服务器数据目录 游戏代码 游戏工程资源目录 /Assets/StreamingAssets 本地数据目录 C:/luaframework (1) 检查本地数据目录 (C:/luaframework), 若不存在目录, 则创建目录并将游戏 工程资源目录 ../Assets/StreamingAssets的内容拷贝到 本地数据目录上。若存在目录,则用本地 file.txt文件与服务器上的file.txt进行MD5比较, files.txt文件是存储资源文件名字和MD5的 (2) 若AppConst.UpdateMode 为True,则进行检测 更新本地数据目录操作 (3) 游戏所需的资 源是从本地数 据目录获取的 游戏工程资源目录 本地数据目录 服务器数据目录 游戏代码
/// LuaFramework.Util.DataPath

/// 
/// 应用程序内容路径
/// AppConst.AssetDir = "StreamingAssets"
/// 
public static string AppContentPath() {
    string path = string.Empty;
    switch (Application.platform) {
        case RuntimePlatform.Android:
            path = "jar:file://" + Application.dataPath + "!/assets/";
        break;
        case RuntimePlatform.IPhonePlayer:
            path = Application.dataPath + "/Raw/";
        break;
        default:
            path = Application.dataPath + "/" + AppConst.AssetDir + "/";
        break;
    }
    return path;
}

/// 
/// 取得数据存放目录
/// 
public static string DataPath {
    get {
        string game = AppConst.AppName.ToLower();
        if (Application.isMobilePlatform) {
            return Application.persistentDataPath + "/" + game + "/";
        }
        if (AppConst.DebugMode) {
            return Application.dataPath + "/" + AppConst.AssetDir + "/";
        }
        if (Application.platform == RuntimePlatform.OSXEditor) {
            int i = Application.dataPath.LastIndexOf('/');
            return Application.dataPath.Substring(0, i + 1) + game + "/";
        }
        return "c:/" + game + "/";
    }
}

完了之后,再进行远程的更新检测,看看用不用热更lua代码,远程url就是AppConst.WebUrl,先下载files.txt,然后再读取lua文件列表进行下载。

(5) GameManager游戏管理器

启动框架后,会创建GameManager游戏管理器,它负责检测lua逻辑代码的更新检测和加载(Main.lua是在LuaManager中执行的),我们可以在GameManager中取DoFile我们自定义的lua脚本,比如Game.lua脚本。

(6) C#中如何直接调用lua的某个方法

GameManager可以获取到LuaManager对象,通过LuaManager.CallFunction接口调用。
也可以用Util.CallMethod接口调用,两个接口的参数有差异,需要注意。

/// LuaManager.CallFunction接口
public object[] CallFunction(string funcName, params object[] args) {
      LuaFunction func = lua.GetFunction(funcName);
      if (func != null) {
          return func.LazyCall(args);
      }
      return null;
  }

/// Util.CallMethod接口
public static object[] CallMethod(string module, string func, params object[] args) {
    LuaManager luaMgr = AppFacade.Instance.GetManager(ManagerName.Lua);
    if (luaMgr == null) return null;
    return luaMgr.CallFunction(module + "." + func, args);
}
(7) lua中如何调用C#的方法

假设现在我们有一个C#类

using UnityEngine;

public class MyTest : MonoBehaviour
{
    public int myNum;

    public void SayHello()
    {
        Debug.Log("Hello,I am MyTest,myNum: " + myNum);
    }

    public static void StaticFuncTest()
    {
        Debug.Log("I am StaticFuncTest");
    }
}

我们想在lua中访问这个MyTest类的函数。首先,我们需要在CustomSettings.cs中的customTypeList数组中添加类的注册:
_GT(typeof(MyTest)),
然后然后再单击菜单【Lua】-【Generate All】生成Wrap,生成完我们会看到一个MyTestWrap
Unity使用tolua框架教程: LuaFramewrk_第8张图片
接下来就可以在lua中访问了。(注意AppConst.LuaBundleMode的值要设为false,方便Editor环境下运行lua代码,否则需要先生成AssetBundle才能运行)

function Game.TestFunc()
    -- 静态方法访问
    MyTest.StaticFuncTest()
   
    local go = UnityEngine.GameObject("go")
    local myTest = go:AddComponent(typeof(MyTest))
    
    -- 成员变量
    myTest.myNum = 5
    -- 成员方法
    myTest:SayHello()
end

调用Game.TestFunc()
在这里插入图片描述
注意,静态方法、静态变量、成员变量、成员属性使用 “.” 来访问,比如上面的 myTest.myNum,成员函数使用 “:” 来访问,比如上面的 myTest:SayHello()

(8) lua中如何使用协程
function fib(n)
    local a, b = 0, 1
    while n > 0 do
        a, b = b, a + b
        n = n - 1
    end

    return a
end

function CoFunc()
    print('Coroutine started')    
    for i = 0, 10, 1 do
        print(fib(i))                    
        coroutine.wait(0.1)                     
    end 
    print("current frameCount: "..Time.frameCount)
    coroutine.step()
    print("yield frameCount: "..Time.frameCount)

    local www = UnityEngine.WWW("http://www.baidu.com")
    coroutine.www(www)
    local s = tolua.tolstring(www.bytes)
    print(s:sub(1, 128))
    print('Coroutine ended')
end

调用

coroutine.start(CoFunc)

Unity使用tolua框架教程: LuaFramewrk_第9张图片
如果要stop协程,则需要这样

local co = coroutine.start(CoFunc)
coroutine.stop(co)
(9) lua解析json

假设现在有这么一份json文件

{
    "glossary": {
        "title": "example glossary",
                "GlossDiv": {
            "title": "S",
                        "GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
                                        "SortAs": "SGML",
                                        "GlossTerm": "Standard Generalized Mark up Language",
                                        "Acronym": "SGML",
                                        "Abbrev": "ISO 8879:1986",
                                        "GlossDef": {
                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
                                                "GlossSeeAlso": ["GML", "XML"]
                    },
                                        "GlossSee": "markup"
                }
            }
        }
    }
}

假设我们已经把上面的json文件的内容保存到变量jsonStr字符串中,现在在lua中要解析它

local json = require 'cjson'

function Test(str)
    local data = json.decode(str)
    print(data.glossary.title)
    s = json.encode(data)
    print(s)
end

调用Test(jsonStr)
Unity使用tolua框架教程: LuaFramewrk_第10张图片

(10) lua调用C#的托管
// c#传托管给lua
System.Action cb = (s) => { Debug.Log(s); };
Util.CallMethod("Game", "TestCallBackFunc", cb);
-- lua调用C#的托管
function Game.TestCallBackFunc(cb)
    if nil ~= cb then
       System.Delegate.DynamicInvoke(cb,"Hello, I am lua, I call Delegate")
    end
end

在这里插入图片描述

(11) lua通过反射调用C#

有时候,我们没有把我们的C#类生成Wrap,但是又需要在lua中调用,这个时候,可以通过反射来调用。
假设我们有一个C#类:MyClass

// MyClass.cs
public sealed class MyClass
{
    //字段
    public string myName;
    //属性
    public int myAge { get; set; }
	
	//静态方法
    public static void SayHello()
    {
        Debug.Log("Hello, I am MyClass's static func: SayHello");
    }

    public void SayNum(int n)
    {
        Debug.Log("SayNum: " + n);
    }

    public void SayInfo()
    {
        Debug.Log("SayInfo, myName: " + myName + ",myAge: " + myAge);
    }
}

在lua中

-- Game.lua
function Game.TestReflection()
    require 'tolua.reflection'
    tolua.loadassembly('Assembly-CSharp')
    local BindingFlags = require 'System.Reflection.BindingFlags'

    local t = typeof('MyClass')
    -- 调用静态方法
    local func = tolua.getmethod(t, 'SayHello')
    func:Call()
    func:Destroy()
    func = nil

    -- 实例化
    local obj = tolua.createinstance(t)
    -- 字段
    local field = tolua.getfield(t, 'myName')
    -- 字段Set
    field:Set(obj, "linxinfa")
    -- 字段Get
    print('myName: ' .. field:Get(obj))
	field:Destroy()
	
    -- 属性
    local property = tolua.getproperty(t, 'myAge')
    -- 属性Set
    property:Set(obj, 29, null)
    -- 属性Get
    print('myAge: ' .. property:Get(obj, null))
    property:Destroy()
    
    --public成员方法SayNum
    func = tolua.getmethod(t, 'SayNum', typeof('System.Int32'))
    func:Call(obj, 666)
	func:Destroy()
	
    --public成员方法SayInfo
    func = tolua.getmethod(t, 'SayInfo')
    func:Call(obj)
    func:Destroy()
end

调用Game.TestReflection()
Unity使用tolua框架教程: LuaFramewrk_第11张图片

(12) nil和null

nil是lua对象的空,null表示c#对象的空。假设我们在c#中有一个GameObject对象传递给了lua的对象a,接下来我们把这个GameObject对象Destroy了,并在c#中把这个GameObject对象赋值为null,此时lua中的对象a并不会等于nil
如果要在lua中判断一个对象是否为空,安全的做法是同时判断nil和null

-- lua中对象判空
function IsNilOrNull(o)
	return nil == o or null == o
end

你可能感兴趣的:(unity3D,lua,Unity3D)