[UnrealCircle]腾讯 罗谦 | UnLua-UE4下的Lua脚本插件

传送门:[UnrealCircle]腾讯 罗谦 | UnLua-UE4下的Lua脚本插件_哔哩哔哩_bilibili

参考PPT:UnrealCircle921北京PPT_免费高速下载|百度网盘-分享无限制


一. UnLua 基础

1.1 概念

  • UnLua 是一个脚本插件
  • UnLua 不是蓝图的替代,而是一种补充
    • 没有 Asset 预览
    • 不支持 nativization
    • 无法保持内容的引用
  • UnLua 为使用 Lua 编写游戏逻辑提供了支持

 

 

1.2 主要特性

  • 无需胶水代码访问 UCLASS、UPROPETY、UFUNCTON、USTRUCT、UENUM
  • 无辅助代码覆写(Overide)BlueprintEvent
  • 无辅助代码覆写(Overmide) AnimNottfy
  • 无辅助代码覆写(Overide) RepNottfy
  • 无辅助代码覆写(Overide) Input Event
  • 简单完备的静态导出方案
  • 高度优化的 UFUNCTION 调用
  • 高度优化的容器(TAray、TSet、TMap)访问
  • 高度优化的结构体访问
  • 支持 UFUNCTON(带'BlueprintCallable' 或 'Exec' 标签)默认参数
  • 支持自定义的碰撞检测相关枚举
  • 支持编辑器内 Server/Cllent 模拟
  • 支持 Lua 协程中执行 Lateni 函数,同步写法完成异步逻辑
  • 支持根据 Blueprint 类型自动生成 Lua 模板代码

 

 

二. 使用原理

2.1 引擎和 Lua 绑定

  1. 静态绑定
    • 仅一个 Interface 和一个纯虚函数
    • 同时支持蓝图类和 C++ 类

 

  1. 动态绑定
    1. 动态创建的 Object
    2. 动态生成的 Actor

 

 

2.2 Lua 访问引擎

 

  • 无巨量的胶水代码
  • 支持反射体系内的所有数据
  • 手动导出一些最基础的函数
    • UObject.Load、UObject.GetName、UObject.GetClass...
    • UClass.Load、UClass.lsChildOf...
    • UWord.SpawnActor...
  • 手动导出数学库
    • FVector、FVector2D、FVector4、FQuat、FRotator、FTransform...

 

 

  • 访问 UClass

 

  • 访问 UStruct:

 

  • 访问 UEnum

 

  • 访问自定义的碰撞检测相关的 UEnum

 

  • 访问 UProperty

 

  • 访问 Delegate/Multi-cast Delegate/Sparse Delegate

 

  • 访问 UFunction

 

  • UFunction 非常量引用参数处理(原子类型)

 

  • UFunction 非常量引用参数处理(非原子类型)
  • 第一种写法在性能上远远高于第二种

 

  • UFunction 返回值参数处理(原子类型)

 

  • UFunction 返回值参数处理(非原子类型)
  • 在有性能要求的场合,建议使用下面第二、三种写法

 

  • 访问 Latent Function
  • 支持在 Lua 的协程中做 Latent Function 的调用(用同步的写法完成异步的逻辑)

 

  • 访问基础容器(TArray、TMap、TSet)
  • Lua 语言本身不支持模板,UnLua 进行特殊处理后把 C++ 里面的类型信息用 Lua 中的参数代替
    • TArray(0)对应 C++代码中实例 TArray<int 32>
    • TArray(FVector)对应 C++代码中实例 TArray<FVector>
  • 在 Lua 中创建的这些容器实例,在 memory layout 内存的布局上,是和引擎一致的,没比要再进行容器到 Lua Table 间的转换

 

  • 静态导出反射体系外的数据
  • 均为程序员手动操作控制,如果修改了类(如:修改了函数签名,函数原型发生了变化)又正好要导出,则需要修改两处

 

 

2.3 引擎访问 Lua

  • 使用 UnLua 插件
  • UnLua 插件区别于其他同类 Lua 插件的最大地方:可以覆写

 

  • Override覆写所有 BlueprintEvent:
    • 用 BlueprintImplementableEvent 标记的 UFunction
    • 用 BlueprintNativeEvent 标记的 UFunction
    • 所有蓝图中定义的 Events/Functions
  • 以上三种类型的 UFunction,都可以直接在 Lua 中写同名函数来覆写,而没有任何辅助代码

 

  1. 覆写 BlueprintEvent:
    • 覆写不带返回值的 BlueprintEvent

 

    • 覆写带返回值的 BlueprintEvent

 

  1. 覆写 AnimNotify

 

  1. 覆写 RepNotify

 

  1. 覆写 Input Events

 

 

三. 重要特性

3.1 覆写的实现

  1. Thunk Function 函数
    • 在 4.23 引擎版本下的 Thunk Function 函数:FNativeFuncPtr Func

 

    • 在 4.19 引擎版本前,Thunk Function 函数指针的原型历史:

 

  1. Thunk 函数的调用
    • UFunction 的执行大概率会走到ProcessEvent函数,并最终通过 Thunk Function 的调用实现(如果将 Thunk Function 指针替换为自定义的可调用 Lua 的函数,则可以实现覆写)

 

  1. Thunk 函数替换
    • 一个 public 函数

 

  1. Thunk 函数替换不适合所有场景
    • 对于在蓝图中定义且在蓝图中被调用的函数或 Events,则走CallFunction函数(不会走到ProcessEvent函数),不通过 Thunk 函数的调用,引擎为所有非 native UFunction 定义的默认的 Thunk Function,被写死了,不管换成什么都没用

 

    • 解决此问题的一些“野路子”:
    • 由于ProcessInternal函数是脚本语言,可以对编译好的字节码进行解释执行,在覆写时,动态插入到字节码序列中
    • 自定义新的 Opcode 注册

 

    • Opcode 注入(引擎处理时,会检测 return、nothing)

 

  1. 返回值处理
    • 非 const 引用参数、return 返回值

 

 

3.2 优化的思路

  1. 结构体访问优化
    • 结构体创建

 

    • 直观实现(伪代码)
    • 在内存的操作上:两个 malloc、一个构造函数

 

    • UnLua 实现(伪代码)
    • 在内存的操作上:一个 malloc、一个构造函数

 

    • 创建时节省一次内存分配,GC时节省一次内存释放
    • Userdata 内存布局:为了满足引擎对一些数据结构的结构体边界对齐要求,使用 Padding 填充,缓存友好(不需要二级指针之类的常规处理),但 Padding 会造成一定的内存浪费,影响不大

 

 

  1. UFunction 调用优化
    • 持久化参数缓存
    • 第一次调用时就被分配好了,最后才被释放掉,(避免频繁的 malloc 和 free)

 

    • 为 Native Local 函数返回值参数,预分配缓存(Local 对应 RPC 函数)

 

    • 为 Native Local 函数提供快速调用路径

 

 

  1. 传参优化
    • UFunction 带常量引用参数

 

    • 直观实现(伪代码)

 

    • UnLua实现(伪代码)
    • Memcpy 浅拷贝,经过大量实践有正确性的保证,且对于复杂数据结构(如:含有大量元素的容器),浅拷贝的性能优势巨大

 

 

  1. 非常量引用参数优化
    • UFunction 带非常量引用参数

 

    • 和 C++ 类似的 Lua 调用方式

 

    • 两次浅拷贝(传参和值返回各一次)

 

 

  1. 返回值参数优化
    • UFunction返回非原子类型

 

    • 直观实现:新创建 Userdata 并将其压入 Lua 栈顶
    • UnLua 实现
    • 先创建 Userdata 并将其作为参数传入函数
    • 利用了传参优化
    • 多次调用(例如循环)情况下,避免了大量的 Userdata 创建和 GC,性能优势明显

 

 

3.3 智能语法提示

  • UnLua 插件中,内置了智能语法提示功能

 

  1. 符号信息(反射体系内)
    • 导出单独的 UnLuaIntelliSense 模块
    • 和 UHT 一同工作,在编译时就生成了所有反射体系内数据的符号信息,为智能语法提示信息提供基础,
    • 符号信息位于ProjectDir/Plugins/UnLua/Intermediate/IntelliSense

 

  1. 符号信息(反射体系外)
    • UnLuaIntelliSense Commandlet
    • 符号信息位于ProjectDir/Plugins/UnLua/Intermediate/IntelliSense/StaticallyExports

 

  1. IDE 中的智能语法提示 API(Lua 语法提示插件:EmmyLua)

 

 

3.4 调试

  1. 调试工具:Debug Any Where

 

posted @   哟吼--小文文公主  阅读(757)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示