UnLua-UE4下的Lua脚本插件 Posted 2021-04-10 虚幻引擎
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UnLua-UE4下的Lua脚本插件相关的知识,希望对你有一定的参考价值。
UnLua是一款UE4下特性丰富且高度优化的Lua脚本插件。它遵循UE4的编程模式,简单易上手,UE4程序员 可以零学习成本使用。
https://github.com/Tencent/UnLua
____________________________________________ ____
• 零胶水代码访问引擎反射体系内的所有UCLASS、UPROPERTY、UFUNCTION、USTRUCT、UENUM。
• 零辅助代码覆写(Override)所有'BlueprintEvent' (包括所有用'BlueprintImplementableEvent'或'BlueprintNativeEvent'标记的UFUNCTION和所有蓝图中定义的Event/Function)、RepNotify、AnimNotify、Input Event。
• 完备的静态导出方案,用于导出引擎反射系统之外的类(成员函数、成员变量)、全局函数、枚举。
• 高度优化的UFUNCTION调用,包括持久化参数缓存、优化的参数传递、优化的非常量引用和返回值处理。
• 高效的基础容器(TArray、TSet、TMap)访问,内存布局与引擎一致,无需在Lua Table和容器间转换。
• 支持UFUNCTION(带BlueprintCallable或Exec标签)默认参数。
• 支持Lua协程中执行Latent函数,同步写法完成异步逻辑。
• 支持根据Blueprint类型自动生成Lua模板代码。
________________________________________________
•
实现GetModuleName函数,返回一个Lua文件路径(相对于Content/Script目录)。
________________________________________________
Widget:AddToViewport(
0
)
如果UFunction参数有默认值(UFunction必须包含'BlueprintCallable'或者'Exec'标签),Lua代码可以简单写成:
第一种方式最为直观,但是后两种方式更为高效,最后一种方式等价于:
协程中执行Latent函数,同步写法完成异步逻辑:
________________________________________________
覆写(Override)'BlueprintEvent'
• 用BlueprintImplementableEvent标记的UFUNCTION
• 用BlueprintNativeEvent标记的UFUNCTION
________________________________________________
UnLua的显著特色就是可以无辅助代码覆写'BlueprintEvent'、AnimNotify、RepNotify、Input Event,这一特性由以下两种方法实现:
'Func'是UFunction的thunk函数,下面是UFunction的执行:
• UFunction是通过'Func'进行实际的执行;
• 通过UObject::ProcessEvent进行UFunction调用都会实际走到'Func'的执行。
所以如果将引擎默认设置的thunk函数替换成一个调用Lua函数的自定义thunk函数,那么我们就可以达到用Lua函数覆写UFunction的目的,而'BlueprintEvent'、AnimNotify、RepNotify以及Input Event恰好都是通过UObject::ProcessEvent进行调用的。
对于非Native UFunction,除了UObject::ProcessEvent,还可以通过UObject::CallFunction调用:
由上图(各版本的引擎会有区别)可以看到UObject::CallFunction并不像UObject::ProcessEvent一样通过UFunction::Invoke调用thunk函数,而是直接调用'ProcessInternal'(引擎为非Native UFunction设置的默认thunk函数,它会对Opcode进行解释执行),所以替换thunk函数并不能满足要求。
这种情况下,我们可以通过向UFunction注入新的Opcode达到覆写的目的:
________________________________________________
UnLua的实现高度优化,尤其体现在以下几个方面。
在Lua中创建结构体实例时,其直观的实现往往类似(伪代码):
两者比较可以看出后者节省了一次内存分配,同理对应Userdata进行GC时也会节省一次内存释放,而且结构体的访问更为缓存友好(cache friendly)。
其中Padding的引入是为了满足结构体的对齐要求。
UnLua为UFunction的调用做了很多优化,使得其性能优于其他同类插件。
每个UFunction的所有参数所占内存大小都是固定的,因此,相对于每次调用时分配,调用结束后释放,分配一块持久的参数缓存要有效的多。
对于Native Local(相对于RPC)函数,在持久化参数缓存基础之上,UnLua还提供了两个方面的优化:
这两方面的优化,避免了UObject::ProcessEvent每次执行都分配、复制参数缓存,以及分配返回值参数相关缓存的消耗。
UnLua对传参和返回值处理做了细致的优化,在UFunction带有复杂数据结构(例如结构体、 TArray等容器)引用参数情况下,其调用性能大幅度领先于其他同类插件。
上图中的UFunction带有一个常量引用参数,其调用的直观实现往往类似(伪代码):
两者对比可以看出,UnLua使用的是浅拷贝,因为浅拷贝已经能够保证正确性了,对于容器这样的复杂数据结构,尤其容器中含有大量元素的情况下,浅拷贝的性能优势是巨大的。
上图中的UFunction带有一个非常量引用参数,和传参利用浅拷贝类似,UnLua对它的调用实现也是基于两次浅拷贝(传参和值返回各一次)完成的。开发者可以使用和C++类似的方式在Lua中调用:
上图中的UFunction返回一个常量FVector引用,直观的实现是每次调用它时,新创建一个Userdata并把它压入Lua栈顶,调用方式如下:
除了支持这种实现,UnLua还提供了一种更高效的实现,即先创建Userdata,再将它作为参数传入函数。调用方式和带非常量引用参数的UFunction类似:
这种方式虽然不如常规方式直观,但在多次调用(例如循环)的情况下,却有明显的性能优势,因为这种方式不仅利用了传参优化,并且避免了大量的Userdata创建和GC。
________________________________________________
UnLua提供了两种方式用于导出智能语法提示相关的符号信息。
• 反射体系内符号UnLuaIntelliSense模块与UHT一同工作,在编译时自动导出所有反射体系内数据的符号信息(位于ProjectDir/Plugins/UnLua/Intermediate/IntelliSense)。
UnLuaIntelliSenseCommandlet用于通过命令行导出反射体系外静态导出数据的符号信息(位于ProjectDir/Plugins/UnLua/Intermediate/IntelliSense/StaticallyExports)。
基于VSCode我们开发了内部的集成开发环境G6IDE,它为开发者提供了完善的智能语法提示、脚本语法检查等功能,使Lua代码编写更加简单高效。
G6IDE还提供了Lua脚本的调试功能(DebugAnyWhere),它是一个调试云服务,可以调试远程手机和服务器上的脚本代码。
G6 Team : G6是腾讯CROS体系下,致力于游戏研发(中间件)技术平台建设的团队。
如需获得更多虚幻引擎4的授权合作方式和技术支持,请发送邮件至
EGC-Business@epicgames.com咨询;
如果你想来 Epic 工作,扫描下方二维码关注我们后点击菜单栏按钮“更多”并选择“招聘”,即可了解我们的最新招聘信息。Epic Games 欢迎你的加入!
“虚幻引擎”微信公众账号是 Epic Games 旗下 Unreal Engine 的中文官方微信频道,在这里我们与大家一起分享关于虚幻引擎的开发经验与最新活动。
以上是关于UnLua-UE4下的Lua脚本插件的主要内容,如果未能解决你的问题,请参考以下文章
lua能做像TC或按键精灵那样的脚本吗?
Redis与Lua脚本
Xposed插件dump Cocos2d-x应用的lua脚本
wireshark插件开发 - Lua插件解析
利用Lua脚本语言制作魔兽WOW插件
Lua脚本语言简单学习