Unity-XLua学习笔记
XLua是腾讯研发的Lua开源插件。
Lua文件加载
执行字符串
最基本是直接用LuaEnv.DoString执行一个字符串,字符串得符合Lua语法。
//定义一个lua环境的全局变量
LuaEnv luaenv = null;
void Start()
{
luaenv = new LuaEnv();
//lua环境执行语句
luaenv.DoString("print('hello world!')");
//释放lua
luaenv.Dispose();
}
加载Lua文件
用lua的require函数即可
//定义一个lua环境的全局变量
LuaEnv luaenv = null;
void Start()
{
luaenv = new LuaEnv();
//加载lua文件
luaenv.DoString("require 'byfile'");
}
void Update()
{
if (luaenv != null)//检测当前luaenv是否为空
{
luaenv.Tick();
}
}
void OnDestroy()
{
luaenv.Dispose();//释放lua
}
byfile.lua.txt文件(lua语法)
print('hello world')
自定义Loader
require实际上是调一个个的loader去加载,有一个成功就不再往下尝试,全失败则报文件找不到。
目前xLua除了原生的loader外,还添加了从Resource加载的loader,需要注意的是因为Resource只支持有限的后缀,放Resources下的lua文件得加上txt后缀。
//定义一个lua环境的全局变量
LuaEnv luaenv = null;
void Start()
{
luaenv = new LuaEnv();
//添加自定义Loader方法
luaenv.AddLoader(MyLoader);
//加载名为xxx的lua文件
luaenv.DoString("require 'xxx'");
}
//自定义Loader方法
private byte[] MyLoader(ref string filePath)
{
print("调用自定义Loader");
//找到你想要查找的路径
string path = Application.dataPath + "/" + "xxx/" + "xxx/" + filePath + ".lua.txt";
//通过UTF-8编码读取文件中的内容
return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(path));
}
void OnDestroy()
{
luaenv.Dispose();//释放lua
}
xxx.lua.txt文件
print("我是xxx/xxx/xxx.lua")
C#访问Lua
这里指的是C#主动发起对Lua数据结构的访问。
获取一个全局基本数据类型
访问LuaEnv.Global就可以了,上面有个模版Get方法,可指定返回的类型。
//定义一个lua环境的全局变量
LuaEnv luaenv = null;
void Start()
{
luaenv = new LuaEnv();
//加载lua文件
luaenv.DoString("require 'byfile'");
//通过c#得到 lua的全局变量
int a = luaenv.Global.Get<int>("a"); print(a);
double b = luaenv.Global.Get<double>("b"); print(b);
string c = luaenv.Global.Get<string>("c"); print(c);
bool d = luaenv.Global.Get<bool>("d"); print(d);
luaenv.Dispose();
}
byfile.lua.txt文件
a = 100
b = 1.11
c = "hello world"
d = true
访问一个全局的table
映射到普通class或struct
public class ByFile : MonoBehaviour
{
//定义一个lua环境的全局变量
LuaEnv luaenv = null;
void Start()
{
luaenv = new LuaEnv();
//加载lua文件
luaenv.DoString("require 'byfile'");
//通过c#得到 lua的全局变量
Person p = luaenv.Global.Get<Person>("person");
print(p.name + "/" + p.age);
p.name = "lisi";
luaenv.DoString("print(person.name)");
luaenv.Dispose();
}
}
class Person
{
public string name;
public int age;
}
byfile.lua.txt文件
person = {
name = "zhangsan",
age = 18,
}
映射到一个interface
这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。
public class ByFile : MonoBehaviour
{
//定义一个lua环境的全局变量
LuaEnv luaenv = null;
void Start()
{
luaenv = new LuaEnv();
//加载lua文件
luaenv.DoString("require 'byfile'");
//通过c#得到 lua的全局变量
IPerson p = luaenv.Global.Get<IPerson>("person");
print(p.name + "/" + p.age);
p.name = "lisi";
luaenv.DoString("print(person.name)");
p.eat();
p.add(10 , 20);
luaenv.Dispose();
}
}
[CSharpCallLua]
public interface IPerson
{
string name { get; set; }
int age { get; set; }
void eat();
void add(int a, int b);
}
byfile.lua.txt文件
person = {
name="zhangsan",
age=18,
eat=function()
print("我正在吃饭");
end
}
function person:add(a,b)--默认带一个self参数,代表当前table等价c#里的this
print(a+b)
end
--[[
function person.add(self,a,b)
print(a+b)
end
--]]
踩坑经历,由于版本不兼容问题,这里加上[CSharpCallLua],并不是所有版本会自动生成代码,每次对byfile.lua.txt文件进行修改需要,右键项目,
进行代码清理,回到unity 点击XLua菜单 Clear Generated code,再点击Generate code,运行即可。
如果上述方法还不成功,请在需要[CSharpCallLua]的方法或委托等,在前面添加public。
更轻量级的by value方式:映射到Dictionary<>,List<>
byfile.lua.txt文件
person = {
name="zhangsan",
age=18,
eat=function()
print("我正在吃饭");
end
,1,2,3,4,1.1,3.2,"student",true,
}
function person:add(a,b)--默认带一个self参数,代表当前table等价c#里的this
print(a+b)
end
Dictionary<string, object> dic = luaenv.Global.Get<Dictionary<string, object>>("person");
foreach (string key in dic.Keys)
{
print(key + "==" + dic[key]);
}
List<object> list = luaenv.Global.Get<List<object>>("person");
foreach (object o in list)
{
print(o);
}
另外一种by ref方式:映射到LuaTable类
LuaTable tab = luaenv.Global.Get<LuaTable>("person");
print(tab.Get<string>("name"));
访问一个全局的function
写完代码,需要回到unity 点击XLua菜单 点击Generate code,运行即可。
Action<int,int> act1 = luaenv.Global.Get<Action<int,int>>("Sum");
act1(10,20);
act1 = null;
byfile.lua.txt文件
function Sum(a,b)
print("调用Sum函数");
print(a+b);
return a+b;
end
映射到delegate
这种是建议的方式,性能好很多,而且类型安全。缺点是要生成代码(如果没生成代码会抛InvalidCastException异常)。
delegate要怎样声明呢?
对于function的每个参数就声明一个输入类型的参数。
多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。
参数、返回值类型支持哪些呢?都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。
delegate的使用就更简单了,直接像个函数那样用就可以了。
public class ByFile : MonoBehaviour
{
//定义一个lua环境的全局变量
LuaEnv luaenv = null;
void Start()
{
luaenv = new LuaEnv();
//加载lua文件
luaenv.DoString("require 'byfile'");
Sum act1 = luaenv.Global.Get<Sum>("Sum");
int resa; int resb;
int res = act1(10, 20, out resa, out resb);
print(res);print(resa);print(resb);//返回多个值
act1 = null;
luaenv.Dispose();
}
}
[CSharpCallLua]
public delegate int Sum(int a, int b, out int c, out int d);
byfile.lua.txt文件
function Sum(a,b)
print("调用Sum函数");
print(a+b);
return a+b,a,b;
end
映射到LuaFunction
这种方式的优缺点刚好和第一种相反。
使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。
LuaFunction luaFunction = luaenv.Global.Get<LuaFunction>("Sum");
object[] os = luaFunction.Call(10, 20);
foreach (object o in os)
{
print(o);
}
提示
1、访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。
2、如果lua测的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。
Lua调用C#
//定义一个lua环境的全局变量
LuaEnv luaenv = null;
void Start()
{
luaenv = new LuaEnv();
luaenv.DoString("require 'byfile'");
luaenv.Dispose();
}
new C#对象
--构造游戏物体, new对象
CS.UnityEngine.GameObject("new by lua")
访问C#静态属性,方法
--读静态属性
print(CS.UnityEngine.Time.deltaTime)
--写静态属性
CS.UnityEngine.Time.timeScale = 0.5
local gameObject = CS.UnityEngine.GameObject
--调用静态方法
local camera = gameObject.Find("Main Camera")
--更改摄像机名称
camera.name = "update by lua"
小技巧:如果需要经常访问的类,可以先用局部变量引用后访问,除了减少敲代码的时间,还能提高性能
访问C#成员属性,方法
--构造游戏物体, new对象
--CS.UnityEngine.GameObject("new by lua")
--读静态属性
print(CS.UnityEngine.Time.deltaTime)
--写静态属性
CS.UnityEngine.Time.timeScale = 0.5
local gameObject = CS.UnityEngine.GameObject
--调用静态方法
local camera = gameObject.Find("Main Camera")
--更改摄像机名称
camera.name = "update by lua"
--调用成员方法的时候 ,使用冒号
local cameraCom= camera:GetComponent("Camera")
--否则需要把自己当做第一个参数传递
--local cameraCom= camera.GetComponent(camera,"Camera")
--删除Camera组件
gameObject.Destroy(cameraCom)