[UE C++] 资源加载(三) 同步加载——LoadObject

文章详细介绍了UEC++中几种常见的同步资源加载方法,如LoadObject、LoadClass和LoadPackage,强调了LoadObject的执行流程,包括StaticLoadObject和StaticLoadObjectInternal的调用。在LoadObject过程中,首先检查内存中是否存在资源,然后尝试加载Package,如果找不到,会检查重定向并尝试跟随。LoadPackage函数则负责加载整个Package。文章提供了使用示例,并提到了FStreamableManager的相关同步加载方法将在后续文章分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[UE C++] 资源加载(三) 同步加载——LoadObject

常用的同步加载方法有,LoadObjectLoadClassLoadPackageFSoftObjectPath::TryLoadFStreamableManager::RequestSyncLoadFStreamableManager::LoadSynchronous等。其中LoadObject是最具代表性的加载过程,以下顺序是以LoadObjectLoadClass的执行流程来介绍。其它API执行流程读者可看下图进行参考,或者进入源码查看。
LoadObject

FStreamableManager::RequestSyncLoadFStreamableManager::LoadSynchronous等同步加载方法,放到下一篇文章中和异步加载一起分析。使用示例在文章末尾。

1. LoadObject

作用: 首先会在内存中查找目标资源是否存在,若存在就会直接返回,若不存在会把路径转化为Package再进行LoadPackage

LoadObject就是对StaticLoadObject进行模板化

template< class T > 
inline T* LoadObject( UObject* Outer, const TCHAR* Name, const TCHAR* Filename=nullptr, uint32 LoadFlags=LOAD_None, UPackageMap* Sandbox=nullptr )
{
	return (T*)StaticLoadObject( T::StaticClass(), Outer, Name, Filename, LoadFlags, Sandbox );
}

输入参数:

  • Outer: 资源的Outer,一个可选对象,用于缩小查找/加载对象的位置
  • Name: 对象的字符串名称。如果它不是完整的引用路径,需要指定Outer和/或Filename
  • Filename: 文件名称,可以从其中加载(或在文件的Package对象中找到)
  • LoadFlags: 控制如何处理从磁盘加载的Flags,ELoadFlags enum
  • Sandbox: 用于限制搜索对象的Package的列表,参数用于网络通信

2. StaticLoadObject

内部调用StaticLoadObjectInternal,若没找到则会进行报错信息输出

3. StaticLoadObjectInternal

首先会调用ResolveName,这个函数上篇文章提到过,作用就是对传入的Path进行解析,这个Path必须是完整路径或者是相对于传入的Outer的路径,随后会将Outer设置为Path所指的Package对象,StrName设置为Path所指对象的NamePrivate。

这里与StaticFindObject调用ResolveName不同的是,传入的后两个bool值为true,代表会尝试Load或Create不存在于内存中的Package以及当找不到Package时会抛出警告

// break up the name into packages, returning the innermost name and its outer
ResolveName(InOuter, StrName, true, true, LoadFlags & (LOAD_EditorOnly | LOAD_NoVerify | LOAD_Quiet | LOAD_NoWarn | LOAD_DeferDependencyLoads), InstancingContext);

而后调用StaticFindObjectFast在内存中寻找资源对象,并判断对象是否加载完成,若完成则return

Result = StaticFindObjectFast(ObjectClass, InOuter, *StrName);
if (Result && Result->HasAnyFlags(RF_NeedLoad | RF_NeedPostLoad | RF_NeedPostLoadSubobjects | RF_WillBeLoaded))
{
    // Object needs loading so load it before returning
    Result = nullptr;
}

若没有在内存中找到目标资源对象,则考虑将资源对象所属的Package加载进内存中

一个包里如果有多个资源,他们在硬盘上对应的是同一个文件,那么只需要加载这个文件就好了

// now that we have one asset per package, we load the entire package whenever a single object is requested
LoadPackage(NULL, *InOuter->GetOutermost()->GetName(), LoadFlags & ~LOAD_Verify, nullptr, InstancingContext);

随后,会再次调用StaticFindObjectFast在内存中寻找资源对象,若还没找到则会判断资源对象是否被重定向了,并通过FindObjectFast查找

// If the object was not found, check for a redirector and follow it if the class matches
if (!Result && !(LoadFlags & LOAD_NoRedirects))
{
    UObjectRedirector* Redirector = FindObjectFast<UObjectRedirector>(InOuter, *StrName);
    if (Redirector && Redirector->DestinationObject && Redirector->DestinationObject->IsA(ObjectClass))
    {
        return Redirector->DestinationObject;
    }
}

4. LoadPackage

作用: 加载与上下文标志匹配的Package和所有包含的对象。

内部调用LoadPackageInternal,输入参数如下(只列举了前三个)

  • InOuter: 如果指定了,则会以InOuter->GetPathName()为加载路径,若为nullptr则会以InLongPackageName为加载路径
  • InLongPackageName: InOuter为nullptr则会以InLongPackageName为加载路径
  • LoadFlags: 控制如何处理从磁盘加载的Flags,ELoadFlags enum

5. LoadPackageInternal

内部实现比较复杂,恕笔者能力不够,没有做分析

最终调用的是LoadPackageAsync函数,这就是异步加载的入口,并且最后FlushAsyncLoading,内部阻塞等待,将异步加载转为同步。
LoadPackageAsync函数,这个函数就是UE4资源加载的大入口,后面整套资源加载都隐藏在了这个函数之后

int32 RequestID = LoadPackageAsync(InName, nullptr, *InPackageName);

if (RequestID != INDEX_NONE)
{
    FlushAsyncLoading(RequestID);
}

Result = (InOuter ? InOuter : FindObjectFast<UPackage>(nullptr, PackageFName));
return Result;

6. LoadClass

作用: 加载类资源

模板化的StaticLoadClass,参数输入和LoadObject完全一致。

template< class T > 
inline UClass* LoadClass( UObject* Outer, const TCHAR* Name, const TCHAR* Filename=nullptr, uint32 LoadFlags=LOAD_None, UPackageMap* Sandbox=nullptr )
{
	return StaticLoadClass( T::StaticClass(), Outer, Name, Filename, LoadFlags, Sandbox );
}

7. StaticLoadClass

内部调用了LoadObject,随后对根据Path加载得到的UClass类型进行判断,若类型不匹配会报错并返回NULL

UClass* Class = LoadObject<UClass>( InOuter, InName, Filename, LoadFlags, Sandbox );
if( Class && !Class->IsChildOf(BaseClass) )
{
    //报错信息
    ······

    // return NULL class due to error
    Class = NULL;
}
return Class;

使用示列

LoadObject —— no Outer

UTexture2D* TestTexture2D = LoadObject<UTexture2D>(nullptr, TEXT("/Game/StarterContent/Textures/T_Burst_M.T_Burst_M"));

LoadPackage

UPackage* TestPackage = LoadPackage(nullptr, TEXT("/Game/StarterContent/Textures/T_Burst_M"), LOAD_None);

LoadObject —— Outer

UPackage* TestPackage = LoadPackage(nullptr, TEXT("/Game/StarterContent/Textures/T_Burst_M"), LOAD_None);
UTexture2D* TestTexture2D = LoadObject<UTexture2D>(TestPackage,TEXT("T_Burst_M"));

LoadClass —— no Outer

TSubclassOf<AActor> TestClass = LoadClass<AActor>(nullptr, TEXT("Blueprint'/Game/Blueprint/BP_Test.BP_Test_C'"));

LoadClass —— Outer

UPackage* TestPackageTwo = LoadPackage(nullptr, TEXT("/Game/Blueprint/BP_Test"), LOAD_None);
TSubclassOf<AActor> TestClass = LoadClass<AActor>(TestPackageTwo, TEXT("BP_Test_C'"));

图中所涉及的其它同步加载使用方式,可参考[UE C++] 资源加载(一) 硬&软引用加载资源

参考

### 如何在 Unreal Engine C++加载并调用由 Matlab 编译器创建的 DLL #### 加载和初始化 DLL 文件 为了能够在 Unreal Engine 的 C++ 项目中成功加载并调用通过 MATLAB Compiler 创建的 DLL 文件,需遵循特定的过程来确保兼容性和功能性。由于 `msvcr110.dll` 是许多依赖于 Microsoft Visual C++ 运行环境的应用程序所必需的一个重要组件[^1],因此,在尝试集成任何第方库之前,确认目标计算机上已安装适当版本的 Visual C++ Redistributable Package 至关重要。 对于来自 MATLAB 的 DLL 而言,除了上述提到的基础运行时支持外,还需要额外配置 MATLAB Runtime (MCR),这是因为 MCR 提供了必要的计算引擎以便执行封装好的 MATLAB 函数逻辑[^2]。这意味着开发者不仅要在开发环境中设置好路径指向该 DLL 及其关联资源的位置,而且还要保证最终用户的机器也具备相同条件才能顺利运行应用。 #### 实现代码示例 以下是用于加载并调用由 MATLAB Compiler 打包成的 DLL 的简化版 C++ 代码片段: ```cpp #include "Windows/AllowWindowsPlatformTypes.h" #include <windows.h> #include "Windows/HideWindowsPlatformTypes.h" // 声明要导入的功能原型 typedef int (*MyFunctionPtr)(double*, double*); void LoadAndCallMatlabDll() { // 定义DLL名称 FString DllName = TEXT("YourCompiledMatlabLibrary"); // 获取当前模块句柄 HMODULE hModule = GetModuleHandle(nullptr); // 构建完整的DLL路径 FString FullPath; FPaths::Combine(*FPaths::GetProjectPluginsDir(), *DllName, &FullPath); // 动态加载DLL void* pLib = FPlatformProcess::GetDllHandle(*FullPath); if (!pLib) { UE_LOG(LogTemp, Error, TEXT("Failed to load DLL")); return; } // 解析导出函数地址 MyFunctionPtr FunctionToCall = (MyFunctionPtr)FPlatformProcess::GetDllExport(pLib, nullptr, TEXT("your_function_name")); if(FunctionToCall != NULL){ // 准备参数 double InputArray[] = { /* 输入数据 */ }; double OutputArray[sizeof(InputArray)/sizeof(double)]; // 调用函数 int ResultCode = FunctionToCall(InputArray, OutputArray); // 处理返回结果... // 卸载DLL FPlatformProcess::FreeDllHandle(pLib); } } ``` 此段代码展示了如何利用 Windows API 来实现对指定位置下的外部库文件进行装载,并获取其中定义的方法指针进而完成实际调用的操作流程。需要注意的是,这里假设被调用者已经按照标准方式暴露出了可供外界访问接口;同时考虑到不同平台间可能存在的差异性因素影响,建议针对具体情况进行相应调整优化。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MustardJim

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值