
游戏开发逆向工程【免费下载链接】RE-UE4SSInjectable LUA scripting system, SDK generator, live property editor and other dumping utilities for UE4/5 games项目地址https://gitcode.com/gh_mirrors/re/RE-UE4SS点击查看免费下载FindAllOf是 RE-UE4SS 注入式 Lua 脚本系统中用于检索 UObject 实例的全局函数它接收一个类的短名称Short Name扫描整个游戏的全局对象数组GUObjectArray返回该类的所有非默认实例。本文将以 官方文档 为骨架结合 Lua 绑定实现 与项目内置 Mod 的真实用法完整讲解该函数的参数、返回值、性能陷阱以及三种基于 SuperStruct 链做类型过滤的进阶写法帮助你在 Mod 开发中安全、高效地定位任意对象。一、函数定位与核心语义1.1 它在 RE-UE4SS 中的位置FindAllOf与FindFirstOf同属于 RE-UE4SS 向 Lua 暴露的全局函数即不挂在任何表上、可直接调用的顶层函数。在 Lua 引擎初始化阶段二者在 UE4SS/src/Mod/LuaMod.cpp 中通过lua.register_function注册FindFirstOf对应源码中的lua.register_function(FindFirstOf, ...)返回单个对象FindAllOf对应lua.register_function(FindAllOf, ...)返回对象表。两者最终都汇入 C 层的Unreal::UObjectGlobals::FindAllOf / FindFirstOf后者在项目内部被大量复用例如UE4SSProgram.cpp 中通过UObjectGlobals::FindAllOf(STR(PlayerController), player_controllers)获取玩家控制器列表BPMods.cpp 通过UObjectGlobals::FindAllOf(mod_actor_name, mod_actors)枚举蓝图 ModActor 实例用于 GUI 渲染LiveView.cpp 将FindFirstOf作为 Watch 的 AcquisitionMethod对象重获取方式之一与StaticFindObject并列可选。这说明FindAllOf / FindFirstOf不只是面向 Lua 脚本作者也是 RE-UE4SS 自身 GUILiveView、BPMods、UFunctionCallerWidget 等检索对象的底层基础设施。1.2 官方定义按 findallof.md 的描述FindAllOf函数将查找所给类名的全部非默认non-default实例。该函数不能用于查找非实例对象也不能查找默认实例default instance即 CDO——Class Default Object。也就是说它遍历的是全局对象数组中的活对象Actor、UObject、UClass 等运行时实例都在其检索范围内但类默认对象Class Default Object如Engine.Engine的 CDO以及纯类型定义本身不属于检索目标。二、参数与返回值2.1 参数#类型说明1string要查找实例的类的短名称Short Name所谓短名称即不含路径前缀的类名例如Actor、Light、PlayerController而非/Script/Engine.Actor这种完整路径。传入完整路径名会导致匹配失败。2.2 返回值#类型子类型说明1nil 或 tableUObject、UClass 或 AActor若未找到任何实例则返回nil否则返回一个从 1 开始索引的数值表包含所有匹配实例返回值的形态可以在 Lua 绑定源码中精确印证。看 LuaMod.cpp 中FindAllOf的实现constexpr int32_t elements_to_reserve 40; std::vectorUnreal::UObject* found_unreal_objects; // Reserving some space because FindAllOf is likely to find lots of objects found_unreal_objects.reserve(elements_to_reserve); Unreal::UObjectGlobals::FindAllOf(lua.get_string(), found_unreal_objects); if (!found_unreal_objects.empty()) { LuaMadeSimple::Lua::Table table lua.prepare_new_table(elements_to_reserve); for (size_t count{}; const auto unreal_object : found_unreal_objects) { // Increasing the count first, this is to accommodate the one-index based tables of Lua count; table.add_key(count); LuaType::auto_construct_object(lua, unreal_object); table.fuse_pair(); } table.make_local(); } else { lua.set_nil(); }三个关键点预分配 40 个槽位由于FindAllOf大概率会命中大量对象绑定层预先reserve(40)以减少扩容开销返回 1 基索引表源码注释明确说明count是为了适配 Lua 从 1 开始的索引约定空结果返回 nilfound_unreal_objects.empty()时直接lua.set_nil()所以脚本里必须先判空再遍历。每个表元素通过LuaType::auto_construct_object自动构造为对应的 Lua 类型——按 LuaUObject.cpp 的分派逻辑实际对象会被包装为UObject、UClass、UScriptStruct、UDataTable、UStruct等对应 Lua 类型且该构造对nullptr安全返回无效对象而非崩溃。三、基础用法示例官方文档给出的最简示例——输出所有继承自Actor类的对象名称local ActorInstances FindAllOf(Actor) if not ActorInstances then print(No instances of Actor were found\n) else for Index, ActorInstance in pairs(ActorInstances) do print(string.format([%d] %s\n, Index, ActorInstance:GetFullName())) end end要点拆解FindAllOf(Actor)会命中所有继承自Actor的实例注意是继承自不是仅精确匹配Actor类本身详见下文第四节结果可能为nil因此先if not ActorInstances判空再遍历是标准姿势ActorInstance:GetFullName()返回带包路径的完整名称如/Game/Maps/MyMap.MyMap:PersistentLevel.SomeLight_0用pairs遍历 1 基索引表在 Lua 5.x 下可行但若要保证顺序更推荐ipairs。项目自带的 UEHelpers.lua 也展示了真实 Mod 中的常见用法——获取玩家控制器时同时回退到父类local Controllers FindAllOf(PlayerController) or FindAllOf(Controller) ---type AController[]?注意这里利用FindAllOf返回nil的特性用or直接做了一次回退查找非常简洁。四、性能为什么说它非常慢官方文档对此毫不讳言这个函数非常慢因为它必须完整遍历一遍整个全局对象数组。4.1 慢的根源FindAllOf的检索范围是引擎持有的全部 UObject包括所有 Actor、组件、资源、蓝图生成的对象等数量动辄数万乃至数十万。每次调用都是 O(N) 全表扫描且对每个对象还要做类型匹配包括向上追溯继承链因此在生产代码如每帧执行的 Tick 逻辑中应尽量避免。若确实要用官方给出以下缓解建议用#运算符先看命中数量例如print(#AllActors)——#对数值表的长度取整是 O(1) 操作命中数量大时不要在遍历循环体内做重活如字符串格式化、反复调用GetFullName优先手动遍历 outer 链、class 链、super 链对链上的每个元素调用UObject::GetFName而不是调用UObject::GetFullName再去解析字符串——因为字符串拼接与解析远贵于 FName 比较把 FNames 缓存到循环外再参与比较或者干脆调用UObject::IsA这是最准确的类型判定详见第五节。4.2 来自 Profilers 的佐证RE-UE4SS 自带的 Profilers 标签页Profilers.cpp中就有专门的性能对比按钮印证了文档的说法Test: FindAllOfAActor/Test: FindAllOfUClass——通过FindObjectSearcherAActor, AnySuperStruct().ForEach(...)走快速路径统计数量Test: Fast Instances——调用UObjectGlobals::FindAllOf(STR(Actor), Instances, true)显式走快速路径Test: Slow Instances——调用UObjectGlobals::FindAllOf(STR(Actor), Instances, false)强制走慢速路径。注意 C 层的FindAllOf有一个额外的布尔参数控制使用快速路径基于索引的FindObjectSearcher还是慢速路径逐对象扫描而 Lua 绑定层只暴露了字符串短名称这一个参数。这也从实现角度解释了文档非常慢的结论Lua 侧无法选择快速路径只能承受全表扫描。4.3 实用建议一次性查询、缓存结果把FindAllOf的结果存到局部/全局变量避免在循环或高频回调中反复调用缩小范围如果能确定目标继承自某个更具体的类就直接用那个类的短名称如用Light而非Actor减少后续过滤工作量低频使用仅在初始化、按键回调、菜单打开等低频时机使用。五、类型过滤进阶SuperStruct 链遍历官方文档强调了一个容易被忽略的事实FindAllOf(Actor)返回的是所有继承自 Actor的实例而不是精确是 Actor的实例。因此当你想对某一具体子类如Light做逻辑时要么直接用更具体的短名称调FindAllOf(Light)要么自己写过滤。5.1 手动遍历 SuperStruct 链下面这段官方示例故意写得复杂目的是展示如何遍历类的 SuperStruct 链表即 Unreal 的继承链来判定对象类型local AllActors FindAllOf(Actor) print(string.format(NumActors found: %s\n, #AllActors)) local LightName UEHelpers.FindOrAddFName(Light) for _, Actor in ipairs(AllActors) do -- Filter on the type. -- The class represents the type. local ActorClass Actor:GetClass() -- Check direct type. local bIsLight false if ActorClass:GetFName() LightName then -- Is directly a Light. bIsLight true end -- Check inheritance. local Super ActorClass:GetSuperStruct() while Super:IsValid() do if Super:GetFName() LightName then -- Inherits from Light. bIsLight true end Super Super:GetSuperStruct() end if bIsLight then -- Do logic on Light. end end关键点Actor:GetClass()拿到该实例的 UClassUClass 即对象的类型ActorClass:GetFName()是当前类的名字与缓存的LightName比较——这就是文档所说缓存 FNames 到循环外比较的实践ActorClass:GetSuperStruct()返回父类然后不断Super Super:GetSuperStruct()沿继承链向上走直到Super:IsValid()为假走到UObject或链表尽头一旦发现链上任何一级的名字等于LightName说明该对象继承自Light。文档还特别点出这种过滤其实常常可以由FindAllOf自己完成——如果你直接把Light传给FindAllOf上面的整个过滤块都可以省略-- 上面一整套 Actor 手动过滤等价于下面这一行 local AllLights FindAllOf(Light)5.2 封装成可复用函数若你需要在多个地方做类型判定可以照抄官方的IsObjectOfType辅助函数。注意它接收的TypeName参数既可以传短名字符串也可以传 FNameGetFName()返回的 FName 与字符串比较在 Lua 绑定中是等价的local function IsObjectOfType(Object, TypeName) -- Filter on the type. -- The class represents the type. local ObjectClass Object:GetClass() -- Check direct type. if ObjectClass:GetFName() TypeName then return true end -- Check inheritance. local Super ObjectClass:GetSuperStruct() while Super:IsValid() do if Super:GetFName() TypeName then return true end Super Super:GetSuperStruct() end return false end官方用它实现了一个按灯光类型分发逻辑的完整示例——先FindAllOf(Light)拿到全部灯光再对每个灯光判定它具体是PointLight、RectLight、SpotLight还是DirectionalLight从而执行不同的分支local AllActors FindAllOf(Light) print(string.format(NumActors found: %s\n, #AllActors)) local PointLightName UEHelpers.FindOrAddFName(PointLight) local RectLightName UEHelpers.FindOrAddFName(RectLight) local SpotLightName UEHelpers.FindOrAddFName(SpotLight) local DirectionalLightName UEHelpers.FindOrAddFName(DirectionalLight) for _, Light in ipairs(AllActors) do if IsObjectOfType(Light, PointLightName) then -- Do logic on PointLight. end if IsObjectOfType(Light, RectLightName) then -- Do logic on RectLight. end if IsObjectOfType(Light, SpotLightName) then -- Do logic on SpotLight. end if IsObjectOfType(Light, DirectionalLightName) then -- Do logic on DirectionalLightName. end end注意这里通过UEHelpers.FindOrAddFName(PointLight)一次性把四个类型名转成 FName 并缓存在循环外循环内只做 FName 比较——正是文档缓存 FNames建议的直接体现。UEHelpers定义于 assets/Mods/shared/UEHelpers/UEHelpers.lua它同时提供了GetEngine内部FindFirstOf(Engine)、GetGameInstance内部FindFirstOf(GameInstance)等基于本函数家族的高频辅助函数值得在写 Mod 时复用。六、最准确的方式IsA StaticFindObjectSuperStruct 名字比较在多数场景够用但官方文档明确指出它并非最准。更准确的做法是用UObject::IsA你也可以使用辅助函数IsA如果你不想自己写过滤器的话。IsA做的是完整的类型检查并且使用类对象而不是名字——这就是为什么你需要自己缓存并传入类对象。它是验证类型最准确的方式。用法差异在于IsA需要的是UClass 对象而非名字所以要先通过StaticFindObject按完整路径把类对象查出来并缓存local AllActors FindAllOf(Light) print(string.format(NumActors found: %s\n, #AllActors)) local PointLightClass StaticFindObject(/Script/Engine.PointLight) local RectLightClass StaticFindObject(/Script/Engine.RectLight) local SpotLightClass StaticFindObject(/Script/Engine.SpotLight) local DirectionalLightClass StaticFindObject(/Script/Engine.DirectionalLight) for _, Actor in ipairs(AllActors) do if Actor:IsA(PointLightClass) then -- Do logic on PointLight. print(string.format(PL: %s\n, Actor:GetFullName())) end if Actor:IsA(RectLightClass) then -- Do logic on RectLight. print(string.format(RL: %s\n, Actor:GetFullName())) end if Actor:IsA(SpotLightClass) then -- Do logic on SpotLight. print(string.format(SL: %s\n, Actor:GetFullName())) end if Actor:IsA(DirectionalLightClass) then -- Do logic on DirectionalLightName. print(string.format(DL: %s\n, Actor:GetFullName())) end end要点StaticFindObject(/Script/Engine.PointLight)需要完整路径/Script/前缀 类全名这与FindAllOf只要短名称形成鲜明对比二者参数形态不要搞混IsA内部由 C 完成继承链判定比手写 SuperStruct 循环更可靠例如能正确处理接口、中间类型等边界情况IsA是最准确的类型验证手段但代价是必须预先取得 UClass——通常在游戏启动后类对象已经加载用StaticFindObject一次取到后缓存即可。七、姊妹函数 FindFirstOf 速览与FindAllOf同源的FindFirstOf只返回第一个非默认实例参数与返回值对应关系如下详见 findfirstof.md#类型说明1string要查找实例的类的短名称返回单个UObject / UClass / AActor仅在找到实例时有效未找到时返回的对象是无效对象不是nil所以官方示例用:IsValid()判空local CharacterInstance FindFirstOf(Character) if not CharacterInstance:IsValid() then print(No instance of class Character was found.) end注意与FindAllOf的差异后者未命中返回nil前者未命中返回无效对象判空方式不同。FindFirstOf同样源自Unreal::UObjectGlobals::FindFirstOfLuaMod.cpp 中的绑定通过LuaType::auto_construct_object(lua, object)构造结果——注释明确说明该构造是nullptr 安全的找不到时也会产出一个IsValid()为假的占位对象。八、实战决策清单最后把本文要点整理成一份可直接照做的决策清单场景推荐做法需要全部实例且是低频操作初始化、按钮回调FindAllOf(短名称)先判nil再遍历需要第一个实例Engine、GameInstance 等单例FindFirstOf(短名称):IsValid()判空只关心某个具体子类直接把子类短名称传给FindAllOf例如FindAllOf(Light)需要最精确的类型判定StaticFindObject缓存 UClass 后调用:IsA(Class)高频/每帧调用不要用FindAllOf改为缓存结果、缓存 FName、遍历时只做轻量比较命中数量很大用#查看数量循环体内避免GetFullName等字符串开销FindAllOf是 RE-UE4SS 脚本体系中无差别全量检索的利器理解其全表扫描、返回 1 基索引表、空则 nil的语义配合 SuperStruct 链遍历或IsA精确过滤你就能在游戏运行时稳定地定位并操作任意对象。相关 API 的完整索引可继续查阅 lua-api.md 与 SUMMARY.md。赞分享游戏开发逆向工程【免费下载链接】RE-UE4SSInjectable LUA scripting system, SDK generator, live property editor and other dumping utilities for UE4/5 games项目地址https://gitcode.com/gh_mirrors/re/RE-UE4SS点击查看免费下载相关推荐UE4SS 全局函数 NotifyOnNewObject 详解监听 UE4/UE5 对象构造与回调注销UE4SS 全局函数 NotifyOnNewObject 详解监听 UE4/UE5 对象构造与回调注销 NotifyOnNewObject 是 UE4SS 注游戏开发逆向工程UE4SS 的 DumpAllObjects 全局函数全量对象与属性转储指南UE4SS_ObjectDump.txt 生成详解UE4SS 的 DumpAllObjects 全局函数全量对象与属性转储指南UE4SS_ObjectDump.txt 生成详解 DumpAllObject游戏开发逆向工程UE4SS LoadAsset 全局函数详解在游戏线程中按路径加载 UE4/5 资产UE4SS LoadAsset 全局函数详解在游戏线程中按路径加载 UE4/5 资产 LoadAsset 是 UE4SS 提供的 Lua 全局函数用于在游戏游戏开发逆向工程上一篇CANN/metadef获取输出偏移量下一篇CANN/metadef获取必选输出格式创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考