XLua基础

一、认识XLua

Xlua是一个插件,用于实现C#和lua语言的交互。
为什么要学习XLua?
通常我们使用XLua方案实现热更新,也就是基于Xlua插件来实现热更新方案。像这样类似功能的插件还有ulua、tolua等等。
热更新就是:当我们增加新功能,我们不需要重新安装下载安装包,就可以更新游戏内容

为什么使用Lua语言?
lua:跨平台、不需要编译(解释型语言),直接生成安装包。因此使用它,避免了编译的开销。

二、XLua实践

1、运行在C#环境lua代码

    private LuaEnv luaenv;
    private void Start()
    {
        luaenv = new LuaEnv();//创建lua运行环境

        luaenv.DoString("print('Hello World')");//运行lua程序
        luaenv.DoString("CS.UnityEngine.Debug.Log('Hello World')");//lua调用C#方法
    }

会直接打印到unity控制台中,与Debug.Log不同的是有一个Lua前缀

git上的官方快速入门有这样一条建议
一个LuaEnv实例对应Lua虚拟机,出于开销的考虑,建议全局唯一。

在实际环境中通常是需要加载lua程序载入到unity中进行使用,而不像上面那样将代码直接写在编辑器中
我们可以先用Resources加载lua文件,读取里面的代码并使用

    void Start()
    {
        LuaEnv env = new LuaEnv();

        //1、使用Resource的方式加载lua文件
        TextAsset ta = Resources.Load<TextAsset>("002/helloworld.lua");
        env.DoString(ta.text);
        //2、使用require的方式进行加载
        env.DoString("require '002/helloworld'");
        env.Dispose();
    }

我们也可以通过自定义loader的方式,来扩展require的功能。比如默认require的加载路径是在Resources文件夹之下,我们可以通过自定义的方式进行修改

    void Start()
    {
        LuaEnv env = new LuaEnv();
        env.AddLoader(CusLoader);
        env.DoString("require 'test007'");
        env.Dispose();
    }

    private byte[] CusLoader(ref string filePath)
    {
        print(filePath);
        //string s = "print(123)";
        print(Application.streamingAssetsPath);

        string absPath = Application.streamingAssetsPath + "/" + filePath + ".lua.txt";
        return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(absPath)); //需要返回字节形式
    }

2、C#访问lua

获取lua中定义的全局变量

int age=env.Global.Get<int>("age");//获取到lua中的全局变量age
string name=env.Global.Get<string>("name");//获取到lua中的全局变量name

建立映射
lua中的代码如下:

age=100
name="baga"

person={
	name="lzy",
	age=100
}

(1)建立lua中table到C#中类和Struct的映射
先定义一个类,类名和lua中table的key值对应上

public class Person
{
    public string name;
    public int age;
}
//1、表到类的映射
        Person p = env.Global.Get<Person>("person");

值得注意的是,这种方式建立的映射实际上是一种值传递,相当于通过lua中的值来new一个新的类
想要获得引用,就需要通过下面这种方式建立映射
(2)建立lua中的table和C#中Interface的映射

[CSharpCallLua] //一定要加上这个特性
public interface IPerson
{
    string name { get; set; }
    int age { get; set; }
}

此时仅仅修改IPerson对象的值,也可以一并修改lua中全局表结构的值

//2、表到接口的映射
IPerson p = env.Global.Get<IPerson>("person");
p.name = "???";
env.DoString("print(person.name)");
print(p.age);
print(p.name);

高版本的unity可能还会发生报错,可通过以下方式解决
https://www.jianshu.com/p/0849b9a46cbd

也可以通过这两种方式映射内部函数

person={
	name="lzy",
	age=100,
	eat=function()
		print("吃个饭")
	end
}

--[
function person:pay(money,count) --不用多写一个self参数
	print(money*count)
end
--]

function person.pay(self,money,count) --需要多写一个self参数
	print(money*count)
end

映射到C#中的接口结构如下

public interface IPerson
{
    string name { get; set; }
    int age { get; set; }
    public void eat();
    public void pay(int money,int count);
}

调用函数方法

p.eat();
p.pay(2, 5);

另外,还可以使用luaTable的方式来建立映射,缺点就是性能慢

(3)将表映射到Dictionary和List下

        //表到Dictionary、List的映射
        Dictionary<string, object> dict = env.Global.Get<Dictionary<string, object>>("person");
        foreach (string key in dict.Keys)
        {
            print(key + "-" + dict[key]);
        }

        //List只能映射对应类型的值数据
        List<object> list = env.Global.Get<List<object>>("num");
        foreach(object o in list)
        {
            print(o);
        }
person={
	name="lzy",
	age=100,
	eat=function()
		print("吃个饭")
	end,
}
num={
	1,2,0.5,1.55,true,false
}

调用全局函数的方法
利用委托来接收lua中的全局函数
lua

function Hello()
	print("Hello")
	return 0,1 --lua可以有多个返回值
end

C#

[CSharpCallLua]
    delegate int Hello(out int resa,out int resb);
        //4、访问全局函数
        Hello action = env.Global.Get<Hello>("Hello");
        int resa;
        int resb;
        int res=action(out resa, out resb);//报错是因为Action有引用 使用out来接收多个返回值
        action = null; //释放delegate
        print(res);
        print(resa);
        print(resb);

有一个报错,就是当释放delegage和env.Dispose放在同一个方法内时会出错。
https://www.codenong.com/js58d20d46560a/
还可以使用luaFunction的方式,但是性能较慢

3、lua访问C#

(1)lua访问C#的静态属性和方法、成员属性等
lua:

--构造游戏物体,new 对象
--CS.UnityEngine.GameObject()

--访问静态属性和方法
local GameObject=CS.UnityEngine.GameObject
print(CS.UnityEngine.Time.deltaTime)
camera=GameObject.Find("Main Camera")
print(camera.name)

--访问成员属性和方法
camTrans=camera:GetComponent("Transform")--使用:可以不用将成员作为第一个参数传入
print(camTrans.position)

C#

    void Start()
    {
        LuaEnv env = new LuaEnv();

        env.DoString("require '005/LuaCallCSharp'");

        env.Dispose();
    }

(2)其他类型
Lua也同样支持C#的访问父类、重载、可变参数、枚举、委托、事件等等
略...参考Xlua中的Xlua教程

三、XLua热更新方案

先前介绍了,就是使用lua代码来操作unity中的组件和各种游戏物体,利用lua来开发unity的功能。那么热更新是如何实现的?

要更新的有两部分:资源(打包成了Assetbundle) and 代码
AssetBundle和lua代码放到了服务端,当玩家连接到服务器,检查AssetBundle和Lua代码更新,如需要更新就把新内容下载到本地。
此外使用MD5校验,可以检查哪些内容需要更新。

posted @   fjnloo  阅读(681)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示