XLua基础
Xlua是腾讯研发的一款Lua开源插件,为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便的和C#相互调用,在游戏中,该技术多用于热更新。可以在GitHub上搜索XLua进行下载,如果网速太慢,也可以在gitee上下载
C#执行Lua脚本
我们在学习每一个课程的时候,最先接触的都是“Hello world”,在XLua中,如何执行该语句呢?将下载的Xlua项目打开,并且创建一个新的脚本,引入XLua命名空间,并挂载到游戏物体。
    //声明一个Lua虚拟机
    public static LuaEnv lua;
    private void Start()
    {
        //对虚拟机进行实例化
        lua = new LuaEnv();
        //用C#的UnityEngine.Debug.Log打印日志
        lua.DoString("CS.UnityEngine.Debug.Log('Hello world')");
        lua.Dispose();
    }
此时,运行项目,可以看到在控制台中打印出了 Hello world
Tips💁♂:一个LuaEnv实例对应Lua虚拟机,出于开销的考虑,建议全局唯一。
 通过Lua.DoString函数,我们可以在Lua语言中快速执行C#代码,但是当Lua脚本内容较多时,将不是很方便执行,这时可以将脚本写在Lua文件中,并C#中执行.
 首先在Unity中创建Resources文件夹,并在里面创建一个txt文本文件,命名为"Demo1.lua.txt"(其要求的是一个文本文档,文件名中的.lua并没有其他含义,仅仅是起标识作用),在文本中加入CS.UnityEngine.Debug.Log("Hello world,load file"),接下来加载该文件
private void Start()
{
    //对虚拟机进行实例化
    lua = new LuaEnv();
    //方式一:通过TextAsset加载
    TextAsset asset = Resources.Load<TextAsset>("LuaDemo1.lua");
    lua.DoString(asset.text);
    
    //方式二:通过Require加载(常用),只能使用Resources与内置的路径的lua文件,不能自定义路径
    lua.DoString("require 'LuaDemo1'");
    
    //方式三:自定义Loader加载,使用Require加载有不能自定义路径等缺点,通过自定义Loader则可以解决上述问题
    lua.AddLoader(DemoLoader);
    lua.DoString("require 'LuaDemo1'");
}
//自定义Loader函数,执行DoString时会自动调用该函数,里面可以实现自己的逻辑
private byte[] DemoLoader(ref string filepath)
{
    string path = filepath + ".lua";
    TextAsset asset = Resources.Load<TextAsset>(path);
    return asset.bytes;
}
获取Lua中的变量
 首先我们修改lua文本文档LuaDemo1中的内容,定义下面两个变量
    x = 123;
    y = "456"
接下来在C#中获取x和y的值,Global是Lua中的一个全局表,里面包含了所有的全局变量
    lua.DoString("require 'LuaDemo1'");
    
    //获取Lua中的值
    int x = lua.Global.Get<int>("x");
    string y = lua.Global.Get<string>("y");
    Debug.Log(x + " , " + y);   //打印 123 , 456
    //修改Lua中的值
    lua.Global.Set("x", 321);
    Debug.Log(lua.Global.Get<int>("x"));   //打印 321
获取Lua中的Table
如果Lua脚本中包含Table,又该怎么调用呢?我们在Lua文本中添加Table,如下所示
    x = 123;
    y = "456"
    
    --定义person表
    person = 
    {
        name = "slayer",
        age = 8,
        "only string",
        999,
        speak = function()
            print("this is a function in Person")
        end
    }
    
    --通过.的方式为person表添加函数
    function person.sayName(self)
        print(self.name)
    end
    --通过:的方式为person表添加函数 等价于 person.getAge(self)
    function person:getAge()
        print(self.age)
    end
C#若要获取Lua中Table值,可以通过类或者接口建立映射,然后获取其值,下面展示如何使用接口获取
    private void Start()
    {
        lua.DoString("require 'LuaDemo1'");
        //上面person表中,对于"only string",999这样单独的值,可以通过List建立映射
        List<object> list = new List<object>();
        list = lua.Global.Get<List<object>>("person");
        foreach (var val in list)
        {
            Debug.Log(val);     //分别打印"only string",999
        }
        IPersonal person = lua.Global.Get<IPersonal>("person");
        Debug.Log(person.name);     //输出 "slayer"
        Debug.Log("Sum:" + person.getSum(12, 34));  //输出 46
    }    
    //定义IPersonal接口,注意这里必须使用 CSharpCallLua 标签
    //并且里面的属性和方法要和Lua脚本中的person表对应
    [CSharpCallLua]
    public interface IPersonal
    {
        string name { get; set; }
        string age { get; set; }
        void sayName();
        int getSum(int num1, int num2);
    }
获取Lua中的函数
我们先在Lua脚本中定义两个函数,如下所示:
    function m_Print(msg)
        print(msg)
    end
    function Add(a,b)
        return a + b
    end
要在C#中调用这两个函数,需要使用委托,对于没有返回值的,可以使用C#自带的Action,有返回值则使用Func,当然也可以自定义委托
    lua.DoString("require 'LuaDemo1'");
    //通过Action获取Lua中的映射,并执行该函数
    Action<string> mPrint = lua.Global.Get<Action<string>>("m_Print");
    mPrint("Hello world");
    Func<int, int, int> add = lua.Global.Get<Func<int, int, int>>("Add");
    int ans = add(1, 2);
    Debug.Log("Add ans:" + ans);    //输出 3
错误信息:InvalidCastException: This type must add to CSharpCallLua:XXXX
 执行上述代码时,可能会出现上述错误,该错误表面当前使用的类型没有加上CSharpCallLua标签,此时我们需要将该标签添加到对应的地方,正常情况下错误将会消失.但有时候,明明添加了该标签,还是提示这个错误,这时通过下面三个步骤一般都可以解决.
- 
方式一:在Unity的Player Setting中将API Compatibility Level设置为 .NET 4.X
 - 
方式二:点击XLua -> Clear Generated Code -> Generated Code
 - 
方式三:在Assets文件夹中,找到XLua -> Examples,找到ExampleGenConfig.cs文件,在CSharpCallLua中按照格式添加你要调用的方法
再不行就只能换一个版本重试了.
 
XLua调用C#静态方法
在上一节中我们学习了怎么通过C#调用XLua打印“Hello world”,那么XLua如何调用C#呢?一行代码轻松搞定
首先创建一个txt文件保存XLua脚本内容,我们命名为“LuaDemo2.lua.txt”,里面的内容如下
--调用C#中Debug.Log函数
--Lua通过CS来访问C#的代码,后面的UnityEngine为命名空间
--CS.UnityEngine.Debug.Log("Hello world")
--在写代码时经常需要调用CS.UnityEngine里面的函数,为了方便我们可以定义一个变量将其保存起来
--使用local关键字定义一个局部变量  下面代码实现的效果与上面相同
local cu = CS.UnityEngine
cs.Debug.Log("Hello world")
--调用其他方法、字段也与之类似,例如
--print(cu.Time.deltaTime)
要执行这行代码,我们创建一个C#脚本,并执行Lua文件
lua.DoString("require 'LuaDemo2'");     //打印Hello world
XLua访问自定义的C#类
在C#中定义一个名为People的类,内容如下:
public class People
{
    public string name;
    public int age;
    public void Show()
    {
        Debug.Log(name + ":" + age);
    }
}
接下来在XLua中对其进行访问
--找到People类并对其进行实例化,等价于:
--local people = CS.People   
--people = people()
local people = CS.People()
people.name = "taylor"
people.age = 23
people:Show()     --打印 taylor:23
XLua查找游戏对象
在unity场景中创建一个Cube,怎么在Lua中修改它的名字呢?
local cug = CS.UnityEngine.GameObject
local cube = cug.Find("Cube")
cube.name = "Change"
 调用脚本后可以看到,场景中的Cube成功改名为Change
XLua实现Unity内置函数,如Awake,Update...
先在Lua脚本中定义一个start函数,函数名称随意
function luaStart()
    print("this is lua start")
end
在C#中调用它,对于lua元表,简单说明一下,点击查看
Lua 查找一个表元素时的规则,其实就是如下 3 个步骤:
1.在表中查找,如果找到,返回该元素,找不到则继续
2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
3.判断元表有没有 index 方法,如果 index 方法为 nil,则返回 nil;如果 index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值。
private void Start()
{
    //加载上面的lua脚本
    asset = Resources.Load<TextAsset>("LuaDemo2.lua");
    //定义一个table
    LuaTable table = lua.NewTable();
    //为table设置元表
    LuaTable metaTable = lua.NewTable();
    metaTable.Set("__index", lua.Global);
    table.SetMetaTable(metaTable);
    metaTable.Dispose();
    table.Set("self", this);
    lua.DoString(asset.text, "LuaDemo2.lua", table);
    
    //通过Action获取lua中的函数
    Action _luaStart = table.Get<Action>("luaStart");
    //执行,Awake和Update类似,只是在不同的地方调用,例如如果实现了_luaUpdate则在Update函数中调用
    _luaStart();
}
XLua控制UI事件
修改前面的luaStart函数为下所示,(注意,C#调用的脚本需要挂在Button按钮上,因为文中使用了self:GetComponent)
--通过lua为unity中的ui添加事件
function luaStart()
    print("This is lua Start")
    input = CUG.Find("InputField")  
    
    --查找到按钮并且为按钮添加事件
    self:GetComponent("Button").onClick:AddListener(function()
        val = input:GetComponent("InputField").text
        if(val == "")
        then
            --print("Can't input nil")
            input:GetComponent("InputField").text = "Can't input nil"
        else
            --print("clicked, you input is '" ..val .."'")
            input:GetComponent("InputField").text = "clicked, you input is '" ..val .."'"
        end
    end)
end
                    
                
                
            
        
浙公网安备 33010602011771号