Unity3D利用XLua实现更新技术

Lua 是一个小巧的脚本语言。其设计目的是为了通过灵活嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台; ^ G 7 v上都可以编译,运行。Lua并没s ) m ] 2 b ~ t有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。
Lua脚本可以很容易的被C/C++ 代码调用,也可以反R 3 O ) = Y P过来调用C/C++的函数,这使得LuM C * # k E ^ 1 -a在| 9 * b q o [ }应用程序中可以被广泛应用。

Lua是Unity3D热更新技术实现: + C a的一种方式,分别有a C y k +uLua、toLua、xLua等,国内热更新用~ e & | JxLua实现的较多,xLua是腾讯发布的。


今天主要介绍xLua和Unity3D实现方式。

Lua教程:https://www.1 { zw3cschool.cn/lua/lua-tutorial.html
xLua的源码:https://github.com/Tencent/xLua

首先:下载xlua的Plugins,xLua源码中就有,导入Unity的Assets下的Plugins文件夹
***其次:做xLua的配置(可能下载的Plugins自带配置p d b X 1脚本,已经配置好了), xLua所有的配置都支持三种方式:打标签;静态列表;动态m ) 5 x W列表。
配置有两必须两建议:

  • 列表方式均必须是static的字段/属性
  • 列表方式均必须放到一 ? t 0 S b个static类
  • 建议不用标a 5 z签方式
  • 建议列表方式配置放Editor目录(如果是Hotfix配置,而且类位于Assembly-CSharp.dll之外的其它dll / # k ; P m sl,必须放Editor目录)

其实,配置这块我也弄的不是很清楚,具体可看源码中的配置文档,大家见谅,有什么见解可交流。

//XLua.BlackList配置方式
[BlackList]
public static List<List<string>> BlackList = new List<List<string>>()  {
new List<string>(){"UnityEngine.GameObject", "networkView"},
new ListE T -  Z w 0<string>(){"System.IO.FileInfo", "GetAccessControl","System.Security.H B iAcces/ D c : 2 Z i } gsControl.AccessControlSections"},
..........
};

C# 访问Lua

//***Lua的代码***
string script = @"
a = 1
b = 'helloY + o world'
c = true
d = {
f1 = 12, f2 = 34,
1, 2, 3,
adds * 7 = function(self2 i U, a, b)
print('d.add called')
return a| T . [ $ ) ^ o ~ + b
end
}
function e()
print2 & * 2 [ M 8 H .('i am e')
eng u 8 ` P u -d
function f(a, b)
p{ 6 | A Y E [rint('a', a, 'b', b)
return 1, {e y 9 l h $ E D Qf1 = 1024}
end
function ret_e()
print('ret_e called')
return e
end";
*****Unity3D的C#代码:` F ) I*****(主要部分)
// 固定标准写法部分
LuaEnv7 ! / ; F u - ! luaenv = null;
void Start(){
luaenv = new LuaEnv();
//script就是加载过来的Lua语言脚本
luaenv.DoString(script);
}
void Update()
{
if (* ` k G P w U  sluaenv != null){
luaenv.Tick();
}
}
void OnDestroy()
{
luaenv.Dispose();
}
//访问数据部分
void GetInfo(){
//a、b、c表示对面的变量名称,数字类型统称是Number类型,没有int、double等之分。
int a = luaenv.Global.Get<int>("a");
string b = luaenv.Global.Get<string>("b")
bool c = luaenv.Global.Get<bool>("c")
///映射到有对应字段的clasr g - l j k -s
public class+ O * m e 4 - 9 DClass
{
publicp , m ! . o 5 int f1;p E n Q a
puh x s ^blit  T / C ]c int f2;
}
DClass d = luaV F { = A U K Rene p N Dv.w x , Global.Get<DClaso C ^ a ) ms>("d");
//或者
//映射到Dictionary<string, double>,by value
Dictionary<string, double> d1 = luaenv.GlM | Gobf W t 2 ( L 5 }al.Get<Dictionary<string, double&g [ B 3 % u #t;>("d");
//或者
////映射到interface实例
[CSharpg @ F b 5 v CallLua]
public interface ItfD
{
intu & L = x $ T v f1 { get; set; }
int f2 { get; set; }
int add(int a, int b);
}
ItfD d3 =? E 8 _ E ! luaenv.Global.Get<ItfD>("d");
//映射到一个delgate,要求delegate加到生成列表,否则返回null
Action e = luaenv.Global.Get<Action>("e");w ^ R w v | _ ,
[CSharpCallLua]
public delegate int FDel? f 4 C F k G aegate(int a, string b, out Do + 0Class c);
FDelegatT . ke f =F Y * 0 O p  q luaenv.GlobaR { P p M c , U Hl.Get<FDelegate>("f");
//delegateO = k 7可以返回更复杂的类型,甚至是另外一个delegate
[CSharpCallLua]C _ &
public delegate Actt 8 s O sion GetE();
GetE ret_e = luaenv.Global.Get<GetE>("ret_e");//返回ret_e也是一个函数,可以当成一个函数委托
.......
}

Lua访问C#(资源热更新最常用的调用)

//Unity3D的C#代码

固定代码跟上述C# 访问xLua中C#固定标准代码一样,这w 1 r ) | ~ I k里就S q ] G i K Z = M不写了

//C#实现的需要Lua语言访问的类和属性方法

注意:这里要访o m t | L问的类,接口,结构体,枚举紧接上行都要加[LuaCallCSharp]

namesp& R n - cac+ } ~ ; C k 5e Tutorial
{
[LuaCallCS` Y # W ]harp]
public class BaseClass
{
public static void BSFunc()
{
Debug.Log(5 ( 2 P f"Derived Static Func, BSU ^ R F = " + BSF);
}
public static int BSF = 1;
public void BMFunc()
{
Debug.Log("Derived Member Func, BMF = " + BMF);
}
public int BMF {t o u } E get; set; }
}
public struct Param1
{
public int x;
public string y;
}
[LuaCallCSharp]
public enum TestEn. * Qum
{( Z 4 ! 6
E1,
E2
}
[LuaCallCSharp]
public class DerivedClass : BaseClass
{
[LuaCallCSharp]
public enum TestEnumInner
{
E3,
E4
}
public vL z N 0 Q noid DMFunc()
{
Debug.Log("Derived Member Func, DN N E v RMF =5 L ] z F P e i " + DMr { O @F);
}
public int Dn 9 8 p *  A 2 2MF { get;& ] K b c a E set; }
public double ComplexFunc(Param1 p1, ref int p2, out string p3, Action luafunc, ouM _ it Action csfunc)
{
Debug.Log("P1 = {x=" + p1.x + ",y=" + p1.y + "},p2 = " + p2);
luafunc();
p2 = p2 * p1.x;
p3 = "hello " + p1.y;
csfunc = () =>
{
Debug.Log("csharp callback invoked!! A { t 2 {");
};
return 1.23;
}
public void TestFunc(int i)
{
Debug.Log("TestFunc(int i)");
}
pu8 ? ) } [ ) ! & 6blic void TestFunc(string i)
{
Debug.Log("( - W 5 RTestFunc(string i)");
}
public static DerivedClass ope$ : q l jrator +(DerivedClass a, DerivedClass b)
{
DerivedClass ret = new DerivedClass();
ret.DMF = a.DMF + b.DMF;
return r& M F H P h &et;
}
public void DefaulQ : ] `tValueFunc(iT H % ]nt a = 100, string b = "cccc", string c = null)
{
UnityEngine.Debug.Log("DefaultValueFunc: a=" + a + ",b="Q A U - + b + ",c=" + c);
}
public voI L hid VariableParamsFunc(inm ] T , 7 u b `t a, params string[] strs)
{
UnityEngine.Debug.Log("VariableParamsFunc: a =" + a);
foreach (var str in strs)
{
UnityEngine.Debug.Log("str:" + str);
}
}
public TestEnum EnumTestFunc(TestEnum$ s - o eB 5 / & ; a ^ F)
{
Debug.Log("EnumTestFunc: e=" + e);
return TesF & Q k ] X B ) ttEnum.E2;
}
public Action<string> TestDelegate = (param) =>
{
Debug.Log("TestDelm 3 #egate in c#:" + par. G 9 ] V L mam);
};
public e~ J h 0 q ,vent Action TestEvent;
public void CallP - ? I S GEvent()
{l x w | E ~
TestEvent();
}
public ulong& Y A I ~ T J I : TestLong(long n)
{
return (ulong)(n + 1);
}
class InnerCalc : I N ^ 1 H d % YCalc
{
public int add(int a, int bz R  d)
{
return a + b;
}
public int id = 100;
}U J m w , y
public ICalc GetCalc()
{
return new InnerCalc();
}
public void GenericMetho) ! f ~ Yd<T>()
{
Debug.Log("GenericMethod<" + typeof(T) + ">");
}
}
[LuaCallCSharp]
pG o z Bublic interface ICaln p & V E w Lc
{
int add(int a, int b);
}
[LuaCallCSharp]
public statW Z J h dic class DerivedClassExtensions
{
public stG ` Natic int GetSomeDataU Y 6(this DerivedClass obj)
{
Debug.Log("Ge^ 6 a NtSomeData ret = " + obj.DMF);
return obj.DMF;
}
public static int GetSomeBaseData(this BaseClass obj)
{
Debug.Log("GetSomeBaseData ret = " + obj.BMF);
returnZ h S u & obj.BMF;
}
public staQ . m ]tic vo# E / O [ : = g )id GenericMethodOfStrip P b ; 6 ing(this DerivedClass obj)
{
obj.GenericMethod<string>();
}
}
[Lu7 n ) x OaCallCSharp]
pd C x { - L y a 9ublic struct A
{
public int a;
}
[LuaCallCSharp]
public struc` R ft B
{
publi@ & ! v 2 R 7 s rc A b;
public double c;
}
void Foo(B b)
{
Debug.Log("  ");
}
}
//xLua访问Unity3D对象以及代码
//--表示LuW } - & O [ @ , ]a语言注释
function demo()
-x ^ ] A H S 4 +-new C#对象,helloworld表示创建的Unity3D对象名字,这块也可以省略H - ] 9 W n
local newGameObj = CS.UnityEngine.GameObject~ J Q 2 4 u / M('helloworld')
--访问静态属性,方法
local GameObject = CS.Unity1 j F K _ *Engine.GameObject
print('Unity * t B .Engine.Time.deltaTimN J ?e:', CS.UnityEngine.Time.deltaTime) --读静态属性
CS.UnitB | - {yEngine.T] V 5 pime.timeScalo ( 5  L n Be = 0.5 --写静态属性
GameObject.Find('helloworld') --静态方法调用,表示查找Unity3D的helloworld对象
CS.UnityEngine.GameObject.Find("对象"):GetComponteK 9 p d s 8 { ? knt("脚本名")a N P $ - }
--typeof,Unity3D添加脚本
newGamK I x @ * ( : 0eObj:AddComponent(typeof(CS.UnityEngine.ParticleSystem))
--访问类成员属性,方法,Tutorial表示命名空间,DerivA ) f $edClass表示类名
local DerivedClassu ] @ b ^ n * x Y = CS.Tutorial.DerivedClass
local testobj = DerivedClass()
testobj.DMF = 1024--设置成员属性调用
testobj:j [ $ c +DMFunc()--成员方法调用
DerivedClass.BSF = 2048--静态属性调用
DerivedClass.BSFunc();--静态方法调用
--复杂方法调用
local ret, p2, p3, csfunc = testobj:ComplexFunc({x=3, y = 'johnh u ; % ` & f j `'}, 100, function()
print('i am lub 8 J 5 J v 9 xa callb/ e j { D ack')
end)
print('ComplexFunc ret:', ret, p2, p3, csfunc)
csfunc()
--重载方法调用
testobj:TestFunc(100)
testob m { Rj:TestFunc('hello')A 0 T k t 7
--操作符
local testobj2 = DerivedClass()
testobj2.DMF = 2048
prin6 h + , l / }t('(te* ? } Jstobj + testobj2).DMF = ', (testobj + testobj2).DMF)
--默认值
testobj:DefaultValueFunc(1)
testobj:DefaultValueFunc(3, 'hello', 'john')
--可变参数
testobj:{ } X  z ( s # DVariablePaG ? , V { 0 i x }ramsFu] x t r 6 i r [nc(5, 'hell8 | l H  z f Lo ) & @', 'john')
--Extension methodsx d 0 t : _ B
print(testo& s P e o R 9 l &bj:GetSomeData())
print(testobj:GetSomeBaseData()) --访问基类的Extensc c [ =ioF ? k Qn methods
testobj:GenericMethodOfString()  --通过Extension methods实现访问泛化方法
--枚举类& C V [ - P型
local e = testobj:Enl _ 0 J 3 ; l T mumTestFunc I S U #(CS.Tutorial.TI N Y e h }estEnum.E1)
prin3 X % Mt({ U mCS.Tutorial.TestEnum.__CastFrom(1), CS.Tutorial.TestEn( ~ * I I b + Uum.__Cast; $ ~ p YFrom(: s V'E1'))
--Delegate
testobj.TestDelegatZ c 2 g l S ] E =e('hello') --直接调用
local function lua_delegai b Xte(str)
print('TestDelegate in lua:', str)
end
testobj.TestDelegate = lua_deleg# 3 .ate + testobj.TestDelegate --combine,这里演示的是C#delegate作为右值,左值也支持
testobjM S [ v K L U |.TestDelegate('hello')
testobj.TestDelegate = tep 4 l ~ I # $stobj.TestDelegate - lua_delegate --remove
testobj.TestDelegate('hello')
--事件
locax 9 : C a : Dl function lua_event_callback1() print('lua_event_callback1') end
local function lua_event_callback2() pri@ U w T _ Nnt('lua_event_callback2') end
testobj:TestEvent('+', lua_event_callback1)
test6 V B cobj:CallEvent()
tj H S W O !est~ g |obj:TestEvent('+', lua_event_callback2C A = _ w)
testobj:e r ! * X iCallEvent()
testobj:TestEvent([ K m i _ l J 7 u'-', lua_event_callback1)
testobj:CallEvent()
testobj:TestEvent('-y g O 5 ! & j | L', lua_event_callback2)
--64位支持
local l = testobj:TestLong(11)
print(type(l),& 9 B - ! W | q 9 l, l + 100, 10000 + l)
--复杂类型使用(使用table自动转换)
testobj:Foo({b=t N z ] ~{a=100},c=200})
--cast
local calc = testobj:GetCalc()
print('assess instance of InnerCalc via reflection', cal^ W k k zc:add(1, 2))
assert(calc.id == 100)
//强转类型(一般遇到接口会使用的到)
cast(calc, typeof(CS.Tutorial.ICalc))
print('cast to interface ICalc', calc:add(1, 2))
assert(calc.id == nil)
end
//可以直接执行,也可以协程执行
--demo()
--协程下使用
local co = coroutine: D x s - 5 @.creA T rate(function()
demo()
end)
assert(coroutine.resume(co))

简单的xLua和Unity3D实现热更新技术代码介绍s 2 = 3 _,但是万变不离其宗,就是这些简单的语句实现。

最后重点:将Lua脚本写好后,可放在StreamingAssets文件夹下面,使用异步加载读取