XLua 教程
执行 Lua 代码
using UnityEngine;
using XLua;
public class Helloworld01 : MonoBehaviour
{
private LuaEnv luaEnv; //对应一个Lua虚拟机,尽量全局唯一
// Start is called before the first frame update
void Start()
{
luaEnv = new LuaEnv();
luaEnv.DoString("print('helloworld')");
luaEnv.DoString("CS.UnityEngine.Debug.Log('Hello World')");
luaEnv.DoString("CS.UnityEngine.MonoBehaviour.print('Hello World')");
}
private void OnDestroy() {
//释放LuaEnv
luaEnv.Dispose();
}
}
输出结果:
LUA: helloworld
Hello World
Hello World
执行的 Lua 代码会再开都有 LUA:
标识。
后两句为在 Lua 中调用 Unity 的 API。
加载 Lua 文件
helloworld.lua.txt
print("helloworld")
a=2 b=3
print(a+b)
using UnityEngine;
using XLua;
public class Helloword02 : MonoBehaviour {
LuaEnv luaEnv;
// Start is called before the first frame update
void Start()
{
TextAsset ta= Resources.Load<TextAsset>("helloworld.lua");
luaEnv = new LuaEnv();
//1.通过Resources加载文本中的内容再去执行
luaEnv.DoString(ta.text);
//2.系统内置方法
luaEnv.DoString("require 'helloworld'"); //require不光引用还会执行,通过loder进行加载helloworld.lua,然后lua虚拟机执行代码
}
private void OnDestroy()
{
luaEnv.Dispose();
}
}
结果:
LUA: helloworld
LUA: 5
LUA: helloworld
LUA: 5
自定义 Loader
using System.IO;
using System.Text;
using UnityEngine;
using XLua;
public class CreateLoader : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
LuaEnv env = new LuaEnv();
//添加自定义Loader
env.AddLoader(MyLoader);
env.DoString("require 'test007'"); //DoString 会依次传递给每一个Loader尝试
env.Dispose();
}
//Loader方法
private byte[] MyLoader(ref string filePath) {
//输出filepath的路径
//print("MyLoader receive filepath:"+filePath);
//string str = "print('I am MyLoder')";
string absPath = Application.streamingAssetsPath + "/" + filePath + ".lua.txt";
string str = File.ReadAllText(absPath);
return Encoding.UTF8.GetBytes(str);
}
}
结果:
LUA: test007
C# 调用 Lua
CSharpCallLua.lua.txt
print('there is CSharpCallLua.lua.txt')
a=100
name="sunny"
isDie=false
person={
name="sunny",age=24,12,2,2,2,2,2,2,3.3,true,"sunny",
eat=function(self,a,b)
print(a+b)
print('我正在吃饭')
end
}
--下面两种方式是相同的
--[[
function person:eat(a,b)--默认代表一个self,代表当前的table
print(a+b)
end
function person.eat(self,a,b)
print(a+b)
end
--]]
function add( )
print("add function")
end
function add1(a,b )
print(a+b)
return a+b,a,b
end
using System;
using System.Collections.Generic;
using UnityEngine;
using XLua;
public class CSharpCallLua : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
LuaEnv luaEnv = new LuaEnv();
luaEnv.DoString("require 'CSharpCallLua'");
print("---获取lua脚本内的变量属性---");
int tempa = luaEnv.Global.Get<int>("a");//获取到CSharpCallLua.lua.txt中的a变量
print(tempa);
string tempname = luaEnv.Global.Get<string>("name");
print(tempname);
bool tempisdie = luaEnv.Global.Get<bool>("isDie");
print(tempisdie);
print("---获取lua脚本内的表(映射class或struct)---");
Person p = luaEnv.Global.Get<Person>("person"); //通过值拷贝进行映射,比较消耗性能,修改这里的属性 ,不会影响lua里边的属性
//lua脚本中不存在age2,所谓age2为默认值0,lua脚本中类似数组的数值也没有映射过来,因为没有对应类型,但是也没有报错,自由度很高
print(p.name + "-" + p.age + "-" + p.age2);
print("---获取lua脚本内的表(映射interface)---");
//此部分在Interface声明添加CSharpCallLua不管用,依旧抛出异常InvalidCastException
IPerson ip = luaEnv.Global.Get<IPerson>("person"); //相当于引用拷贝,修改这里的属性,lua里的属性也会改变
print(ip.name + "-" + ip.age);
ip.name = "sunny_cxr";
//通过在lua中访问此字段看是否更改
luaEnv.DoString("print(person.name)");
//调用lua中的方法
ip.eat(10, 20); //相当于ip.eat(ip,10,20); lua中也需要传递当前对象
print("---获取lua脚本内的表(映射到Dictionary<>,List<>)---");
print("---Dictionary<>---");
Dictionary<string, object> dic = luaEnv.Global.Get<Dictionary<string, object>>("person");
foreach (string key in dic.Keys)
{
print(key + "-" + dic[key]);
}
print("---List<>---");
List<object> list = luaEnv.Global.Get<List<object>>("person");
foreach (object o in list)
{
print(o);
}
print("---获取lua脚本内的表(映射到LuaTable类)---");
LuaTable tab = luaEnv.Global.Get<LuaTable>("person");
print(tab.Get<string>("name"));
print(tab.Get<string>("age"));
print("table.length:" + tab.Length);
print("---访问Lua中的全局函数(映射到delegate)---");
Action act1 = luaEnv.Global.Get<Action>("add");
act1();
act1 = null; //若是不将委托置为空,当LuaEnv注销时act1依旧指向虚拟机中的方法,则会抛出异常
//此部分在Add委托声明添加CSharpCallLua不管用,依旧抛出异常InvalidCastException
Add add = luaEnv.Global.Get<Add>("add1");
int resa; int resb;
int res = add(100, 101, out resa, out resb);
print("res:" + res);
print("resa:" + resa);
print("resb:" + resb);
add = null;
print("---访问Lua中的全局函数(映射到LuaFunction)---");
LuaFunction func = luaEnv.Global.Get<LuaFunction>("add");
object[] os = func.Call(1, 2);
foreach (object o in os)
{
print(o);
}
luaEnv.Dispose();
}
[CSharpCallLua]
delegate int Add(int a, int b,out int resa,out int resb); //lua中的多返回值,可以映射到out、ref参数上
class Person
{
public string name;
public int age;
public int age2;
}
// 用来进行映射的接口需要这个特性
[CSharpCallLua]
interface IPerson
{
string name { get; set; }
int age { get; set; }
void eat(int a, int b);
}
}
Lua 调用 C#
LuaCallCSharp.lua.txt
--构造游戏物体,new对象
CS.UnityEngine.GameObject("new by lua")
print(CS.UnityEngine.Time.deltaTime)
CS.UnityEngine.Time.timeScale=0.5
local camera=CS.UnityEngine.GameObject.Find("Main Camera")
camera.name="update by lua"
local light=CS.UnityEngine.GameObject.Find("Directional Light")
--每次都通过CS.UnityEngine.GameObject访问Find方法比较耗费性能
--声明一次对CS.UnityEngine.GameObject的引用,以后直接使用即可
local gameObject=CS.UnityEngine.GameObject
local camera1=gameObject.Find("update by lua")
camera1.name="again update by lua"
local light1=gameObject.Find("Directional Light")
light1.name="平行光"
--调用成员方法时使用冒号
local cameraCom=camera:GetComponent("Camera")
gameObject.Destroy(cameraCom)
using UnityEngine;
using XLua;
[LuaCallCSharp]
enum EnemyType
{
Normal,
Hard,
NB,
}
public class LuaCallCSharp : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
LuaEnv luaEnv = new LuaEnv();
luaEnv.DoString("require 'LuaCallCSharp'");
luaEnv.Dispose();
}
}
使用 xLua 实现热更新的方案
- 资源—打成AssetBundle
- 代码—使用Lua编写
- 客户端启动连接服务器检查资源更新
- 如果更新就下载解压运行最新的
- 使用 MD5 校验检查文件是否更新,本地对比服务器的,文件是否最新,文件是否存在