XLua基础

  • A+
所属分类:.NET技术
摘要

Xlua是腾讯研发的一款Lua开源插件,为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便的和C#相互调用,在游戏中,该技术多用于热更新。可以在GitHub上搜索XLua进行下载,如果网速太慢,也可以在gitee上下载


XLua简介

Xlua是腾讯研发的一款Lua开源插件,为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便的和C#相互调用,在游戏中,该技术多用于热更新。可以在GitHub上搜索XLua进行下载,如果网速太慢,也可以在gitee上下载

[gitee链接]  https://gitee.com/mirrors/xlua?utm_source=alading&utm_campaign=repo 

 

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;
}

 

C#获取与修改Lua中的值

获取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