尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Unreal Engine集成动态链接库:从原理到跨平台实战

Unreal Engine集成动态链接库:从原理到跨平台实战 1. 项目概述为什么要在Unreal里折腾动态链接库如果你在Unreal EngineUE项目里用过一些第三方库比如OpenCV、TensorFlow Lite或者是一些硬件厂商提供的SDK那你大概率已经和动态链接库DLL、.dylib、.so打过交道了。这活儿听起来基础但真上手了你会发现从“能用”到“稳定好用”之间隔着一片满是坑的沼泽地。我自己就经历过无数次编辑器里跑得好好的一打包就报错“找不到xxx.dll”或者Debug模式一切正常切到Shipping构建直接崩溃更别提那些诡异的“初始化例程失败”了。网上搜到的解决方案很多都是零散的“魔法命令”告诉你“把这个DLL扔到Binaries/Win64里”但很少说清楚背后的原理。为什么放这里为什么有时候放这里也不行不同平台Windows、macOS、Linux的机制有什么不同今天我就结合自己踩过的坑把Unreal集成动态链接库这件事从“黑盒操作”变成“白盒理解”。我们不仅要会配更要明白每一步配置在UE庞大的构建和运行时体系里到底起了什么作用。2. 核心思路理解UE的模块化与依赖管理在开始动手前我们必须跳出“普通C项目”的思维定式。Unreal Engine是一个高度模块化、拥有自己一套构建工具UnrealBuildTool, UBT和运行时资源管理机制的庞然大物。直接往项目里拖DLL文件或者简单修改一下VS的项目属性十有八九会失败。2.1 UnrealBuildTool (UBT) 与 .build.cs 文件UBT是UE的灵魂它负责解析所有模块的依赖关系并生成真正的编译指令如Visual Studio的.sln文件。每个模块都有一个对应的.build.cs文件C#脚本它定义了该模块的“元数据”依赖哪些其他模块、包含哪些头文件路径、链接哪些库等等。当你集成一个纯头文件的库比如glm和一个需要链接的静态库.lib/.a时操作已经有所不同。而动态链接库情况更特殊它涉及编译时依赖头文件和导入库和运行时依赖实际的DLL文件两个层面。关键认知在UE中集成第三方库尤其是动态库主要工作就是正确编写这个库对应模块的.build.cs文件并妥善处理库文件的存放与部署路径。2.2 ModuleRules 与 ModuleType.External在.build.cs中你会继承ModuleRules类。对于没有源代码、只有二进制文件的第三方库无论是静态库还是动态库你需要将其模块类型设置为External。using UnrealBuildTool; public class MyThirdPartyLibrary : ModuleRules { public MyThirdPartyLibrary(ReadOnlyTargetRules Target) : base(Target) { Type ModuleType.External; // 关键声明这是一个外部库模块 // 1. 添加预处理器宏如果需要 PublicDefinitions.Add(WITH_MYTHIRDPARTYLIBRARY1); // 2. 添加头文件包含路径 PublicIncludePaths.Add(Path.Combine(ModuleDirectory, include)); // 3. 添加导入库Windows的.lib或静态库.a // 注意这里链接的是导入库不是DLL本身 PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, lib, MyLib.lib)); } }Type ModuleType.External;这行代码告诉UBT“这个模块没有我自己的.cpp文件需要编译我的作用只是为其他模块提供头文件和库链接信息。” 这是集成二进制库的第一步也是最基础的一步。2.3 运行时依赖DLL去哪了上面代码中的PublicAdditionalLibraries.Add只解决了编译链接问题。链接器Linker需要.libWindows或.so/.dylibUnix-like文件来解析符号但程序运行时操作系统加载器需要找到对应的动态库文件.dll, .dylib, .so。在普通C项目里你可能会设置环境变量PATH或者把DLL放在可执行文件旁边。在UE里事情没那么简单。UE编辑器UnrealEditor.exe和打包后的游戏YourGame.exe启动时有一系列预设的目录搜索顺序。如果你放错了地方系统就找不到你的DLL。UE的运行时库搜索路径Windows示例简化版应用程序所在目录例如Engine/Binaries/Win64/或YourProject/Binaries/Win64/。当前工作目录。Windows系统目录System32,SysWOW64。PATH环境变量中的目录。问题在于你的第三方库模块可能位于插件目录Plugins/YourPlugin/或项目源码目录Source/下。这些目录下的DLL默认不会在构建或打包时被自动复制到最终的二进制输出目录。这就是很多“编辑器运行正常打包后崩溃”问题的根源。3. 平台差异与实战配置详解不同操作系统对动态库的加载机制截然不同这直接影响了我们在.build.cs里的配置策略。3.1 Windows平台DLL与导入库Windows的动态链接模型相对“死板”。每个.exe或.dll都有一个导入表列出了它需要的所有DLL。系统加载程序时会按固定顺序搜索这些DLL。标准集成步骤文件组织在插件或模块目录下创建一个清晰的文件夹结构。我推荐这样组织YourPlugin/ ├── Source/ │ ├── YourPlugin/ │ │ ├── YourPlugin.Build.cs │ │ └── ... │ └── ThirdParty/ │ └── MyLib/ │ ├── include/ (存放 .h 头文件) │ ├── lib/ │ │ ├── Win64/ (存放 .lib 导入库) │ │ └── ... (其他平台) │ └── bin/ │ ├── Win64/ (存放 .dll 动态库) │ └── ... (其他平台).build.cs 配置public class YourPlugin : ModuleRules { public YourPlugin(ReadOnlyTargetRules Target) : base(Target) { // ... 其他依赖声明 ... string ThirdPartyPath Path.Combine(ModuleDirectory, .., ThirdParty, MyLib); // 包含路径 PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, include)); // 平台特定路径 string PlatformSubdir Target.Platform.ToString(); string LibPath Path.Combine(ThirdPartyPath, lib, PlatformSubdir); // 链接导入库 PublicAdditionalLibraries.Add(Path.Combine(LibPath, MyLib.lib)); // 关键声明运行时依赖让UBT在打包时复制DLL string DllPath Path.Combine(ThirdPartyPath, bin, PlatformSubdir, MyLib.dll); // 将DLL复制到目标输出目录如 编辑器下的 Binaries/Win64 RuntimeDependencies.Add($(TargetOutputDir)/MyLib.dll, DllPath); // 同时为了支持打包也声明一个UFS通用文件系统依赖这通常用于非二进制资源但对于确保文件被纳入构建流程也有用 // RuntimeDependencies.Add(DllPath, StagedFileType.NonUFS); // 注意DLL不能放PAK文件必须用NonUFS } }RuntimeDependencies.Add详解这是解决“打包后找不到DLL”的核心。它告诉UBT的部署系统“在构建或打包的最后阶段请把这个源文件DllPath复制到目标位置$(TargetOutputDir)/MyLib.dll。”$(TargetOutputDir)是一个UBT预定义的变量指向当前构建配置Editor、Development、Shipping的可执行文件输出目录。使用FPlatformProcess::GetDllHandle显式加载可选如果你不想依赖系统的自动加载比如想动态加载不同版本的库或者DLL路径比较特殊可以使用UE提供的跨平台函数来加载。// 在需要加载DLL的代码中 void* DllHandle FPlatformProcess::GetDllHandle(TEXT(MyLib.dll)); if (DllHandle) { // 使用FPlatformProcess::GetDllExport获取函数指针 auto MyFuncPtr (MyFuncType)FPlatformProcess::GetDllExport(DllHandle, TEXT(MyFunction)); if (MyFuncPtr) { MyFuncPtr(); } // 最后记得释放 // FPlatformProcess::FreeDllHandle(DllHandle); }GetDllHandle的好处是它内部封装了各平台的加载逻辑并且在Windows上会尝试在UE的二进制目录中搜索DLL比系统默认搜索更友好。同时它的日志输出非常详细对调试加载失败极有帮助。3.2 macOS平台动态库与 rpathmacOS使用dyld作为动态链接器它依赖一个叫做install name的路径这个路径被硬编码在动态库文件里告诉链接器和运行时“去哪里找我”。为了灵活性通常使用rpathruntime path作为前缀。核心任务确保你的.dylib文件的install name是rpath/libfoo.dylib并且可执行文件或调用它的动态库的Runpath Search PathLC_RPATH包含了你的.dylib所在目录。集成步骤修正 Install Name如果你的第三方.dylib的install name是绝对路径如/usr/local/lib/libfoo.dylib你需要修改它。# 查看当前的 install name otool -D libfoo.dylib # 输出可能是/usr/local/lib/libfoo.dylib # 修改为 rpath install_name_tool -id rpath/libfoo.dylib libfoo.dylib这一步必须在将库文件放入你的项目目录之前完成。你可以写一个脚本来自动化这个过程。.build.cs 配置配置与Windows类似但链接的是.dylib文件本身macOS没有单独的导入库概念。if (Target.Platform UnrealTargetPlatform.Mac) { string DylibPath Path.Combine(ThirdPartyPath, lib, Mac, libfoo.dylib); // 直接链接动态库文件 PublicAdditionalLibraries.Add(DylibPath); // 同样需要声明运行时依赖确保文件被复制 RuntimeDependencies.Add($(TargetOutputDir)/libfoo.dylib, DylibPath); }UBT在构建macOS目标时会自动为生成的可执行文件添加必要的LC_RPATH条目通常包括executable_path/../UE4和executable_path等这通常能覆盖项目二进制输出目录。调试工具使用otool和dyld相关命令调试。# 查看可执行文件的 RPATH otool -l YourGame.app/Contents/MacOS/YourGame | grep -A2 LC_RPATH # 查看动态库的依赖 otool -L libfoo.dylib3.3 Linux平台共享对象与 RPATH/RUNPATHLinux使用ld.so作为动态链接器其搜索路径由RPATH或RUNPATHELF文件头中的字段、LD_LIBRARY_PATH环境变量以及系统缓存决定。UE在构建时会为模块设置RPATH。集成步骤.build.cs 配置与macOS高度相似。if (Target.Platform UnrealTargetPlatform.Linux) { string SoPath Path.Combine(ThirdPartyPath, lib, Linux, x86_64-unknown-linux-gnu, libfoo.so); // 可能还需要链接 .so 对应的版本号文件如 libfoo.so.1 PublicAdditionalLibraries.Add(SoPath); RuntimeDependencies.Add($(TargetOutputDir)/libfoo.so, SoPath); }注意符号冲突Linux上有一个特别需要注意的问题。UE模块默认以RTLD_LOCAL标志加载这意味着模块内的全局符号不会暴露给后续加载的模块。如果两个不同的第三方动态库或一个第三方库和引擎模块定义了同名的全局变量或函数它们可能会绑定到不同的地址导致数据不一致或崩溃。这个问题在Windows上由于DLL模型不同不那么常见。如果遇到难以解释的崩溃可以怀疑是这个问题。调试工具# 查看可执行文件或.so的依赖和RPATH readelf -d YourGame | grep -E (RPATH|RUNPATH|NEEDED) # 模拟运行时加载器查找库 ldd YourGame # 使用LD_DEBUG环境变量获得极其详细的加载过程信息慎用输出极多 LD_DEBUGlibs ./YourGame4. 高级技巧与避坑指南掌握了基础配置我们来看看那些容易让人栽跟头的高级场景和解决方案。4.1 延迟加载Delay LoadDLL这是Windows平台独有的特性。有时候你的代码可能只在特定条件下才需要调用某个DLL里的函数。如果这个DLL不一定存在比如某个可选功能的插件或者你希望加快启动速度可以使用延迟加载。原理链接器不会在程序启动时就把这个DLL加载进来而是在代码第一次调用该DLL中的函数时才通过一个小的“桩”函数thunk去加载真正的DLL并跳转到正确的函数地址。配置方法 在.build.cs文件中添加PublicDelayLoadDLLs.Add(OptionalLib.dll);然后正常链接对应的导入库OptionalLib.lib。重要限制不能用于导出变量延迟加载机制只处理函数。如果你的代码直接访问DLL中导出的全局变量链接器会报错因为它需要在启动时就解析这些变量的地址。需要自行处理加载失败第一次调用延迟加载DLL的函数时如果系统找不到DLL会抛出异常。你的代码需要准备好捕获这个异常通常是SEH异常并进行降级处理。实操心得延迟加载非常适合那些“锦上添花”的功能库。比如一个负责生成高质量截图的水印库如果找不到程序完全可以不用水印继续运行。但对于核心功能库如物理引擎、渲染后端不建议使用延迟加载因为启动时的确定性加载失败比运行时的偶发崩溃更容易定位问题。4.2 处理Windows.h与宏冲突很多Windows原生库或跨平台库的Windows版本会包含windows.h。UE为了保持代码的跨平台性和避免宏污染默认不直接包含这个头文件。它通过WindowsHWrapper.h进行了一层包装。正确做法// 在需要Windows API的.cpp文件中 #include Windows/WindowsHWrapper.h // 现在可以安全地包含第三方头文件了如果它内部包含了windows.h #include ThirdPartyHeader.h为什么WindowsHWrapper.h在包含真正的windows.h之前会先定义一些保护性宏防止像min,max,TRUE,FALSE这样的宏污染你的代码。如果你直接包含windows.h很可能会和UE自身的宏定义冲突导致编译错误。极端情况如果第三方库的代码必须使用被UE重定义的宏比如它直接用了TRUE你可以使用“隔离区”#include Windows/AllowWindowsPlatformTypes.h // 在这段代码里Windows的原始宏被暂时恢复 int windowsFlag TRUE; // 使用Windows定义的TRUE #include Windows/HideWindowsPlatformTypes.h // 出了这个区域UE的定义重新生效但这种情况应尽量避免最好能修改第三方库的代码或寻找替代品。4.3 打包与部署RuntimeDependencies的妙用RuntimeDependencies是控制文件如何进入最终打包目录Staging Directory和Pak文件的强大工具。前面我们用它来复制DLL但它远不止于此。常见场景与配置将DLL复制到可执行文件旁必须这是最基本用法确保运行时能找到。RuntimeDependencies.Add($(TargetOutputDir)/MyLib.dll, SourceDllPath);将资源文件放入Pak文件UFS对于配置文件、模型、纹理等资源可以打包进.pak文件通过虚幻虚拟文件系统访问。RuntimeDependencies.Add(Path.Combine(PluginDir, Resources/Config.ini), StagedFileType.UFS);将文件松散部署NonUFS有些文件必须作为独立文件存在比如某些需要被外部进程读取的日志文件、或者一些安装程序。RuntimeDependencies.Add(Path.Combine(PluginDir, Extras/License.txt), StagedFileType.NonUFS);平台特定部署你可以根据目标平台决定部署哪些文件。if (Target.Platform UnrealTargetPlatform.Win64) { RuntimeDependencies.Add($(TargetOutputDir)/MyLib.dll, Win64DllPath); } else if (Target.Platform UnrealTargetPlatform.Mac) { RuntimeDependencies.Add($(TargetOutputDir)/libMyLib.dylib, MacDylibPath); } // Linux同理避坑提示StagedFileType.UFS的文件会被打包进.pak。动态链接库绝对不能使用UFS因为操作系统的加载器无法从虚拟文件系统中读取DLL。DLL、EXE等可执行文件必须使用NonUFS或通过$(TargetOutputDir)隐式指定。4.4 调试“DLL初始化例程失败”等经典错误搜索词里提到的“OSError: [WinError 1114] 动态链接库(DLL)初始化例程失败”是一个典型的Windows错误。它通常发生在DLL的DllMain函数中。DllMain是DLL的入口点在加载和卸载时被调用。如果里面的代码尤其是DLL_PROCESS_ATTACH阶段崩溃了就会抛出这个错误。排查思路依赖项缺失这是最常见的原因。你的DLL可能依赖另一个DLL而那个DLL没找到。使用Dependency WalkerDepends.exe老牌但经典或微软的dumpbin /dependents命令来检查你的DLL依赖了哪些其他DLL。dumpbin /dependents MyLib.dll确保所有列出的DLL都存在于应用程序的搜索路径中。C运行时库CRT冲突如果你的DLL是用/MD动态链接CRT编译的而你的UE项目特别是Shipping构建可能使用/MT静态链接CRT或者反之就会导致堆内存管理混乱在DllMain中分配/释放内存时崩溃。务必确保第三方库和你的UE项目使用相同的CRT链接方式。通常与UE配合第三方库也应使用动态链接CRT/MD或/MDd。在DllMain中做了危险操作微软明确警告在DllMain中应避免调用LoadLibrary、创建线程、调用用户态同步对象等操作因为加载器锁Loader Lock可能被持有。如果第三方库的DllMain写得不好就可能触发这个问题。这个问题很难从外部解决可能需要联系库的供应商提供修复版本。使用UE的日志如前所述FPlatformProcess::GetDllHandle在失败时会输出非常详细的日志到输出日志Output Log中。打开UE编辑器在运行游戏时查看输出日志搜索你的DLL名字往往能直接看到“无法找到依赖模块 XYZ.dll”这样的明确信息。使用Process Monitor这是微软SysInternals套件里的神器。它可以实时监控系统所有的文件、注册表、进程活动。过滤你的游戏进程名然后观察它在启动时尝试打开哪些DLL文件在哪里“找不到文件”NAME NOT FOUND或“访问被拒绝”ACCESS DENIED能让你一目了然地看到加载过程。5. 实战案例集成一个虚构的“加密库”假设我们要集成一个名为CryptoHelper的第三方动态库它提供简单的字符串加密功能。它有Windows.dll, .lib、macOS.dylib和Linux.so三个平台的版本。第一步文件准备与组织从供应商那里拿到库文件后我们首先检查macOS的.dylib的 install name并用install_name_tool修正为rpath/libCryptoHelper.dylib。然后将文件组织如下MyProject/Plugins/MyCryptoPlugin/ ├── Source/ │ ├── MyCryptoPlugin/ │ │ ├── MyCryptoPlugin.Build.cs │ │ ├── MyCryptoPlugin.h │ │ └── MyCryptoPlugin.cpp │ └── ThirdParty/ │ └── CryptoHelper/ │ ├── include/ │ │ └── CryptoHelper.h │ ├── lib/ │ │ ├── Win64/ │ │ │ └── CryptoHelper.lib │ │ ├── Mac/ │ │ │ └── libCryptoHelper.dylib │ │ └── Linux/ │ │ └── x86_64-unknown-linux-gnu/ │ │ └── libCryptoHelper.so │ └── bin/ │ ├── Win64/ │ │ └── CryptoHelper.dll │ ├── Mac/ │ │ └── libCryptoHelper.dylib (与lib下的是同一个或符号链接) │ └── Linux/ │ └── x86_64-unknown-linux-gnu/ │ └── libCryptoHelper.so (与lib下的是同一个或符号链接)第二步编写 .build.cs 文件// MyCryptoPlugin.Build.cs using System.IO; using UnrealBuildTool; public class MyCryptoPlugin : ModuleRules { public MyCryptoPlugin(ReadOnlyTargetRules Target) : base(Target) { PCHUsage ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { Core }); PrivateDependencyModuleNames.AddRange(new string[] { }); // 第三方库路径 string ThirdPartyPath Path.Combine(ModuleDirectory, .., ThirdParty, CryptoHelper); string IncludePath Path.Combine(ThirdPartyPath, include); PublicIncludePaths.Add(IncludePath); // 平台特定配置 if (Target.Platform UnrealTargetPlatform.Win64) { string LibPath Path.Combine(ThirdPartyPath, lib, Win64); PublicAdditionalLibraries.Add(Path.Combine(LibPath, CryptoHelper.lib)); string DllPath Path.Combine(ThirdPartyPath, bin, Win64, CryptoHelper.dll); // 关键声明运行时依赖复制DLL RuntimeDependencies.Add($(TargetOutputDir)/CryptoHelper.dll, DllPath); } else if (Target.Platform UnrealTargetPlatform.Mac) { string DylibPath Path.Combine(ThirdPartyPath, lib, Mac, libCryptoHelper.dylib); PublicAdditionalLibraries.Add(DylibPath); RuntimeDependencies.Add($(TargetOutputDir)/libCryptoHelper.dylib, DylibPath); } else if (Target.Platform UnrealTargetPlatform.Linux) { string SoPath Path.Combine(ThirdPartyPath, lib, Linux, x86_64-unknown-linux-gnu, libCryptoHelper.so); PublicAdditionalLibraries.Add(SoPath); RuntimeDependencies.Add($(TargetOutputDir)/libCryptoHelper.so, SoPath); } else { // 不支持其他平台可以抛出错误或记录警告 System.Console.WriteLine($CryptoHelper plugin does not support {Target.Platform} platform.); } } }第三步编写包装类在MyCryptoPlugin.cpp中我们创建一个简单的包装类来调用库函数。这里假设CryptoHelper.h定义了char* encrypt_string(const char*)和void free_string(char*)两个C接口函数。// MyCryptoPlugin.cpp #include MyCryptoPlugin.h #include CryptoHelper.h // 第三方库头文件 #include HAL/PlatformProcess.h void UMyCryptoPluginBPLibrary::EncryptString(const FString InputString, FString OutEncryptedString, bool bSuccess) { bSuccess false; OutEncryptedString.Empty(); // 确保库已加载系统会自动加载这里显式调用以处理延迟加载或错误 // 实际上由于我们通过.lib/.dylib/.so链接程序启动时就会尝试加载。 // 如果加载失败在调用函数前就会崩溃。更健壮的做法是使用GetDllHandle动态加载。 // 此处为简单演示假设链接时加载成功。 // 转换字符串 std::string StdInputString TCHAR_TO_UTF8(*InputString); // 调用第三方库函数 char* EncryptedCStr encrypt_string(StdInputString.c_str()); if (EncryptedCStr ! nullptr) { OutEncryptedString UTF8_TO_TCHAR(EncryptedCStr); free_string(EncryptedCStr); // 记得释放第三方库分配的内存 bSuccess true; } }第四步测试与打包在编辑器里编写一个简单的蓝图或C代码调用UMyCryptoPluginBPLibrary::EncryptString测试功能是否正常。进行Development和Shipping构建测试。特别注意Shipping构建因为优化级别和CRT链接方式可能不同。使用文件资源管理器或终端检查构建输出目录如MyProject/Binaries/Win64/下CryptoHelper.dll或对应的动态库是否被正确复制过来。最后进行项目打包。打包后在打包输出目录如MyProject/Saved/StagedBuilds/WindowsNoEditor/MyProject/Binaries/Win64/中再次确认动态库文件是否存在。6. 常见问题排查速查表问题现象可能原因排查步骤与解决方案编译成功但编辑器启动或运行时崩溃1. DLL依赖项缺失。2. CRT运行时库不匹配。3. DLL本身有bug如在DllMain中崩溃。1. 用Dependency Walker或dumpbin检查DLL依赖确保所有依赖库都存在。2. 确认第三方库的构建配置Debug/Release, /MD vs /MT与你的UE项目匹配。通常UE项目用/MD和/MDd。3. 尝试使用FPlatformProcess::GetDllHandle动态加载看错误信息。使用Process Monitor监控文件访问。编辑器里运行正常打包后游戏崩溃1. 动态库文件没有被复制到打包目录。2. 打包时包含了错误的平台版本库文件。1. 检查.build.cs中的RuntimeDependencies.Add语句确保源路径和目标路径正确且使用了$(TargetOutputDir)等正确的变量。2. 检查打包后的Binaries目录确认动态库文件是否存在。确认没有把Win64的DLL错误地用于Mac打包。链接错误LNKxxxx1. 导入库.lib路径或文件名错误。2. 函数声明头文件与库文件版本不匹配。3. 缺少必要的库依赖。1. 检查.build.cs中PublicAdditionalLibraries.Add的路径和文件名区分Debug/Release库。2. 确保你包含的头文件版本与你链接的库文件版本一致。3. 有些库可能需要链接多个.lib文件如主库依赖库检查库的文档。“无法定位程序输入点于动态链接库”1. 运行时加载的DLL版本与编译时链接的导入库版本不一致。2. 函数名修饰Name Mangling问题C vs C。1. 确保你部署的DLL文件就是你链接的那个版本。清理中间文件重新生成。2. 如果是C库确保头文件中的函数声明使用了extern C或者调用方使用了正确的命名空间。使用dumpbin /exports YourDLL.dll查看导出的函数名是否与你的调用匹配。macOS上报“Library not loaded”1..dylib的install name不是rpath。2. 可执行文件的RPATH没有包含库所在目录。1. 用otool -D检查.dylib的 install name并用install_name_tool -id修改。2. 用otool -l检查可执行文件的LC_RPATH。确保UBT能正确设置。通常将库放在标准位置如插件Binaries目录并由RuntimeDependencies处理即可。Linux上报“undefined symbol”1. 链接了错误的.so文件版本不对。2. 全局符号冲突RTLD_LOCAL问题。1. 使用readelf -s或nm -D查看.so文件导出的符号确认有你需要的函数。2. 如果怀疑符号冲突可以尝试在调用第三方库之前用dlopen以RTLD_GLOBAL标志显式加载它但这需要修改代码且需谨慎。更根本的方法是避免使用有全局符号冲突的库。集成动态链接库到Unreal Engine是一个将系统编程知识动态链接、ABI与特定引擎框架UBT、模块化、部署相结合的过程。它没有太多“黑科技”更多的是对细节的把握和对不同平台机制的理解。最有效的学习方法就是亲手集成一个库然后故意制造一些错误比如删掉一个依赖DLL或者放错路径观察错误现象再用上面提到的工具去分析和解决。这个过程积累的经验会让你下次再遇到类似问题时能迅速定位到问题的核心。
返回列表