• UE集成第三方库开发技巧、自定义第三方库输出路径


    1、Unreal Engine的插件或模块开发时,ModuleRules的部分属性说明

    官方文档地址:https://docs.unrealengine.com/4.26/zh-CN/ProductionPipelines/BuildTools/UnrealBuildTool/ModuleFiles/

    (1) Type (ModuleType)

    ModuleType有两个枚举值:External和CPlusPlus
    External:
    当将 Type 属性设置为 “external” 时,这意味着该模块是外部的,通常是指与 Unreal Engine 引擎外部的第三方模块或库。这种模块通常不是由 Unreal Engine 自身管理和构建的,而是由外部工具或构建系统管理。“external” 模块可能需要额外的配置和设置,以便正确地与 Unreal Engine 集成。
    CPlusPlus:
    当将 Type 属性设置为 “cplusplus” 时,这意味着该模块是一个标准的 Unreal Engine C++ 模块。这种模块是由 Unreal Engine 自身构建和管理的,通常包含游戏或应用程序的自定义功能。“cplusplus” 模块可以受益于 Unreal Engine 的构建系统和工具,并能够利用 Unreal Engine 的各种功能。
    看起来没什么区别,但是更推荐external。

    (2) PublicIncludePathModuleNames (List)

    包含头文件的模块名称列表(不需要路径),我们模块的公共头文件需要访问这些头文件,但是不需要"导入"或链接它们

    public class MyModule : ModuleRules
    {
        public MyModule(ReadOnlyTargetRules Target) : base(Target)
        {
            // ...
    
            PublicIncludePathModuleNames.AddRange(new string[] { "ModuleA", "ModuleB" });
    
            // ...
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    (3) PublicDependencyModuleNames (List)

    公共依赖性模块名称的列表(不需要路径)(自动执行私有/公共包含)。这些是我们的公共源文件所需要的模块。

    PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Sound" });
    
    • 1
    (4) PrivateIncludePathModuleNames (List)

    包含头文件的模块名称列表(不需要路径),我们模块的私有代码文件需要访问这些头文件,但是不需要"导入"或链接它们。

    (5) PrivateDependencyModuleNames (List)

    私有依赖性模块名称列表。 我们的私有代码依赖这些模块,但我们的公共包含文件的内容均不依赖这些模块。

    (6) PublicSystemIncludePaths (List)

    系统/库包含路径列表——通常用于外部(第三方)模块。 这些是公共的稳定头文件目录,在解析头文件依赖性时不会受到检查。

    (7) PublicSystemLibraryPaths (List)

    系统库路径列表( .lib 文件的目录),对于外部(第三方)模块,请改用PublicAdditionalLibaries。

    (8) PublicIncludePaths (List)

    (当前不需要此设置,因为我们会在‘Public’文件夹中发现所有文件)公开给其他模块的包含文件所有路径的列表。

    (9) PrivateIncludePaths (List)

    此模块内部包含文件的所有路径的列表,不向其他模块公开(至少有一个包含到‘Private’路径,如果要避免相对路径,则会更多)

    (10) PrivateRuntimeLibraryPaths (List)

    运行时各个库的搜索路径列表(例如 .so 文件)。

    (11) PublicRuntimeLibraryPaths (List)

    运行时各个库的搜索路径列表(例如 .so 文件)

    (12) PublicAdditionalLibraries (List)

    附加库的列表(.lib文件的名称,包括扩展名)——通常用于外部(第三方)模块。

    (13) PublicSystemLibraries (List)

    要使用的系统库的列表——这些库一般通过名称引用并通过系统路径查找。如果你需要引用 .lib 文件,请改用PublicAdditionalLibraries。

    (14) PublicDelayLoadDLLs (List)

    用于指定需要在程序运行时延迟加载的DLL文件。延迟加载是指在程序运行时才加载DLL文件,而不是在程序启动时就加载。这可以提高程序启动速度,因为不需要加载所有的DLL文件。不在这个list中的dll会在程序启动时就加载。

    (15) DynamicallyLoadedModuleNames (List)

    用于指定需要在程序运行时动态加载的模块,包括DLL文件和插件。动态加载是指在程序运行时才加载模块,而不是在程序启动时就加载。这可以使程序更加灵活,因为可以根据需要加载不同的模块。

    (16) RuntimeDependencies (RuntimeDependencyList)

    用于指定模块在运行时所依赖的文件列表。这些文件会在构建目标时被一同打包,并在运行时加载,以确保模块的正确运行。

    //添加运行时依赖项列表的文件路径
    RuntimeDependencies.Add("Path/To/My.dll");//这将把My.dll添加到运行时依赖项中
    //第一个参数为要添加的文件的目标路径,第二个参数是原始文件的路径。告诉引擎将原始文件复制到指定的目标路径,以确保运行时可用。
    RuntimeDependencies.Add("TargetPath/release.dll","SourcePath/release.dll");
    
    • 1
    • 2
    • 3
    • 4

    2、Unreal C++开发时的常用路径获取

        FString ProjectPath = FPaths::ProjectDir();//"项目的根路径:F:/CVE/learn/UE5/TestCustomPlugin/"
        FString ProjectConfigPath = FPaths::ProjectConfigDir();//项目配置文件路径:"F:/CVE/learn/UE5/TestCustomPlugin/Config/"
        FString ProjectContentPath = FPaths::ProjectContentDir();//项目的Content路径:"F:/CVE/learn/UE5/TestCustomPlugin/Content/"
        FString ProjectPluginPath = FPaths::ProjectPluginsDir();//项目插件路径:"F:/CVE/learn/UE5/TestCustomPlugin/Plugins/"
        FString ProjectFile = FPaths::GetProjectFilePath();//项目文件的完整路径:"F:/CVE/learn/UE5/TestCustomPlugin/TestCustomPlugin.uproject"
    
        FString EngineDir = FPaths::EngineDir();//引擎的根目录路径:"../../../Engine/"
        FString EngineContentDir = FPaths::EngineContentDir();//引擎Content文件夹路径:"../../../Engine/Content/"
    
        FString BinaryOutputDir = FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Binaries")));//二进制输出目录路径: "F:/CVE/learn/UE5/TestCustomPlugin/Saved/Binaries"
        FString IntermediateDir = FPaths::ProjectIntermediateDir();//中间文件目录路径: "F:/CVE/learn/UE5/TestCustomPlugin/Intermediate/"
        FString ProjectSavedDir = FPaths::ProjectSavedDir();//项目保存数据的目录路径: "F:/CVE/learn/UE5/TestCustomPlugin/Saved/"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    M o d u l e D i r 、 {ModuleDir}、 ModuleDir{PluginDir}、 E n g i n e D i r 、 {EngineDir}、 EngineDir{ProjectDir}、 T a r g e t O u t p t D i r 和 {TargetOutptDir}和 TargetOutptDir{BinaryOutputDir}占位符说明:https://blog.csdn.net/u010385624/article/details/97797461

    3、链接动态库并打包到自定义路径

    using System;
    using System.IO;
    using UnrealBuildTool;
    
    public class GDAL : ModuleRules
    {
        public GDAL(ReadOnlyTargetRules Target) : base(Target)
        {
            PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
            bEnableUndefinedIdentifierWarnings = false;
    
            Type = ModuleType.External;
    
            if (Target.Platform == UnrealTargetPlatform.Win64)
            {
                // 添加头文件所在目录,并暴露给外部模块
                PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "include"));
    
                //添加导入文件
                string importedLibraryPath = Path.Combine(ModuleDirectory, "lib");
                string[] importedLibraries = Directory.GetFiles(importedLibraryPath, "*.lib");
                foreach (string item in importedLibraries)
                {
                    //指定导入库的全路径名这样就不需要指定PublicRuntimeLibraryPaths的值了。
                    //这步也可以指定导入库的文件名,但是就需要指定PublicRuntimeLibraryPaths的值了
                    PublicAdditionalLibraries.Add(Path.Combine(importedLibraryPath, item));
                }
    
                //添加动态库文件
                string dynamicLibraryPath = Path.Combine(ModuleDirectory, "bin");
                string[] dynamicLibraries = Directory.GetFiles(dynamicLibraryPath, "*.dll");
                //PublicRuntimeLibraryPaths.Add(importedLibraryPath);//如果PublicAdditionalLibraries中指定的是导入库文件名,则这里需要指定导入库路径。
    
                foreach (string item in dynamicLibraries)
                {
    
                    string dynamicLibraryName = Path.GetFileName(item);
                    //程序启动后,按需加载dll,没有这段话则在程序一启动时就加载dll文件。同时如果将dll文件输出到指定自定义路径的话,则必须使用按需加载的方式。
                    PublicDelayLoadDLLs.Add(dynamicLibraryName);
                    //将第三方dll库输出到自定义目录下,这样设置时ue会对使用到第三方库的代码进行检查,如果语法存在问题或隐患,可以运行,但是不能打包,打包会报错。比如删除一个指针的时候没有判断指针是否为空,这种潜在问题就会导致打包失败。
                    RuntimeDependencies.Add(Path.Combine("$(PluginDir)/Binaries/Win64/ThirdParty/GDAL/", dynamicLibraryName), item);
                }
            }
            else
            {
                Console.Error.WriteLine("Only support Win64 Platform");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    4、添加PublicDelayLoadDLLs的搜索路径

    上个步骤中,动态链接库被拷贝到插件目录下(“$(PluginDir)/Binaries/Win64/ThirdParty/GDAL/”),但是PublicDelayLoadDLLs默认的加载路径并不包含这个路径,因此要添加该路径到加载路径中,在使用该第三方库插件的插件的StartupModule方法中调用PushDllDirectory方法新增加载路径,例如:

    void FCustomGDALModule::StartupModule()
    {
    	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
    
    	// Get the base directory of this plugin
    	FString BaseDir = IPluginManager::Get().FindPlugin("CustomGDAL")->GetBaseDir();
        //增加第三方库dll的路径以便PublicDelayLoadDLLs中的dll被加载
    	FPlatformProcess::PushDllDirectory(*(BaseDir + "/Binaries/Win64/ThirdParty/GDAL/"));
    	FString dllPath = FPaths::Combine(BaseDir, "/Binaries/Win64/ThirdParty/GDAL/gdal.dll");
        //判断dll文件是否被正确加载,加载了才能成功调用第三方库的方法
    	void* dllHandle = FPlatformProcess::GetDllHandle(*dllPath);
    	if (dllHandle) {
    		CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
    		CPLSetConfigOption("SHAPE_ENCODING", "");
    		GDALAllRegister();
    	}
    	else {
    		FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("ThirdPartyLibraryError", "Failed to load example third party library"));
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    【java筑基】IO流进阶之文件随机访问、序列化与反序列化
    MAX插件CGMAGIC一键解决效果图在软包硬包上费时费力操作!
    React旧有生命周期和新生命周期的解析
    冯喜运:4.26最新外汇黄金美原油走势分析及操作策略
    mmdet3D中文注释
    数据可视化
    折叠始祖摩托罗拉,困于“性价比”折叠屏
    多线程间的通信方式你知道几种?
    祥云杯2022 pwn - bitheap
    Snowflake8亿美元收购Streamlit,联手构建基于数据的应用程序
  • 原文地址:https://blog.csdn.net/weixin_41364246/article/details/133880323