吃鸡手游的成功,让手游多了一种引擎选择:UE4。于是陆续有人来问xLua的UE4版本。要做UE4版本,由于宿主语言的不同其实相当于完全重新开发。我想既然都重新开发了,能否重新考虑当年xLua的一些技术决策点,放在UE,放在那么多年后的今天是否仍然合适。
xLua的开发是在2015年初,那时苹果刚要求应用提交64位版本,unity刚为此做了il2cpp。il2cpp早期占用空间大,而同期iOS应用的允许的代码段却很小(ios7以前版本40M,ios7是60M),去掉引擎本身的占用应用捉襟见肘,那时作为一个第三方库体积是很关键的。而lua在体积方面十分优秀,100K的大小把高级语言的常用特性都支持了,所以lua在当时是个十分恰当的选择。然而到了今天,ios9以上代码段限制是900M,lua在这方面的优势已经不再明显。
宿主语言是C#的话,C#本身有运行时类型校验,也有良好的反射机制,lua的动态类型并没有太大的影响,传错类型(c#的复杂类型在lua侧都以userdata代表)顶多会抛个异常,不会有严重的后果。但在UE4,宿主语言是C++,C++本身没运行时检查,而要在lua侧记录类型信息并动态校验开销会比较大,可能因为这个原因或者别的因素不少lua方案不做校验直接传,这可能会导致十分严重的后果,比如一个c++函数参数要求的是FVector指针,并修改其Z字段,在lua那错传了FVector2D指针,由于C++不会校验指针类型,所以会产生越界写(某ue4的lua方案真实案例)。这种问题能当场crash你应该谢天谢地,最怕它只是修改了别的模块的数据,一系列连锁反应产生些随机的错误,那才是噩梦。但是在996行业背景下,这种昏头操作难以避免。
还有随着lua在游戏项目应用逐渐重度使用,甚至出现所谓的全lua游戏,lua代码量多了,即使在Unity项目其动态类型也逐渐带来一系列问题:
最后一点是lua的生态,严格来说前面说的“静态类型检查”也是生态的衍生品。生态说白了就是有多少人投入进来,人多力量大,像安德斯·海尔斯伯格这种大神可以为js生态创造一个ts,而一般人的使用和反馈bug也是一种贡献:帮来人踩坑。用lua的时候,很多时候你得自己造轮子,自己踩坑。
所以,我在UE4的脚本方案选型,加入了两点要求:
1、支持静态类型检查;
2、有良好的生态:包括工具链,库,文档;
最终我锁定了typescript/javascript,typescript有静态类型,由大神操刀设计,语法优雅,而且IDE有大厂支持,应用很广泛,它最近两年已经跻身github十大活跃语言:
js引擎又该选啥呢?市面上能找到的开源js引擎:V8,jscore,spidermonkey,quickjs,duktape。。。
V8、jscore、spidermonkey分别用在chrome,safari,firefox浏览器。都是久经考验。
而以quickjs,duktape为代表的小众js引擎优点是体积小,300k ~ 600K,然后没太多特别的地方,或者quickjs对标准的支持比较激进算一个,甚至还在提案中的操作符重载都支持了。
最终选了v8:
稳定性是最主要的因素,程序稳定了,才能考虑其它的,v8已经在chrome浏览器各平台经过了无数人的蹂躏,无数时间的考验。况且它的性能也十分优秀。
生态也是考量之一,比如各IDE都有V8的调试支持,这对开发来说也是至关重要的。
体积么,V8在各架构上大概是8M~16M,虽说比quickjs大,但在如今问题不大,可能一次小增量更新就不止这数。
提起V8,很多人会觉得很重,觉得内存占用会比较大,然而实测android下一个简单demo占2M内存。群内有人提“v8感觉太大了,怕手机内存吃不消”,chrome浏览器各手机平台都有,也没见谁说开个浏览器就吃不消,何况浏览器占大头的应该也不是v8。
puerts的ue4在今年年初就已经内部发布,目前已经在一个项目里头实际应用了大半年。而unity版本则是今年5月开发完毕,重用了部分xLua的代码,比如对象池,反射,通过泛型实现delegate的支持。