UE4关卡流式加载与Latent机制
讨论一下UE4中的关卡流式加载。
基础的介绍和使用见官方文档就行了,这里就不再赘述。
这里假定你已经学会怎么用了,而且知道怎么在C++里面使用关卡流式加载。那么,本篇文章来讨论一下关卡流式加载在UE4中是怎么实现的。
异步
说到异步,用大白话可以这么说:我发起一个异步行为,并把完成后的回调也传给它;行为执行完毕之后,回调会被调用。
就像关卡流式加载这里,在发起异步加载关卡的时候,需要传入一个FLatentActionInfo
结构体,里面放入回调信息。包括必要的:
- 回调执行对象(
this
) - 回调要执行的函数(
OnSceneLoaded
)
void UTest::LoadScene()
{
FLatentActionInfo LatentInfo;
LatentInfo.CallbackTarget = this;
LatentInfo.ExecutionFunction = "OnSceneLoaded";
LatentInfo.UUID = 1234;
LatentInfo.Linkage = 1;
// 两个布尔值参数分别代表:加载完成后是否马上显示出来、加载时是否阻塞
UGameplayStatics::LoadStreamLevel(this, TEXT("/Game/Scene/TestSubLevel"), true, false, LatentInfo);
}
void UTest::OnSceneLoaded()
{
UE_LOG(LogTemp, Log, TEXT("UTest::OnSceneLoaded()"))
}
说起来,UUID是全局的为了避免重复行为的一个uid,所以使用
FLatentActionInfo
的时候自己维护一个全局的UUID管理器。
行为发起!
接下来就是发起一个流式加载的行为了,这部分代码在UGameplayStatics::LoadStreamLevel()
中。
void UGameplayStatics::LoadStreamLevel(const UObject* WorldContextObject, FName LevelName, bool bMakeVisibleAfterLoad, bool bShouldBlockOnLoad, FLatentActionInfo LatentInfo)
{
if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
{
FLatentActionManager& LatentManager = World->GetLatentActionManager();
if (LatentManager.FindExistingAction<FStreamLevelAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr)
{
FStreamLevelAction* NewAction = new FStreamLevelAction(true, LevelName, bMakeVisibleAfterLoad, bShouldBlockOnLoad, LatentInfo, World);
LatentManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, NewAction);
}
}
}
这里的代码其实很好懂了。
首先判断有没有存在这个行为,如果已经存在了就不再追加。
若不存在该行为,那么使用回调信息(使用结构体FLatentActionInfo
中的信息)创建一个FStreamLevelAction
行为。
FStreamLevelAction
继承于FPendingLatentAction
行为基类。在这一节先知道这个就好了,后面再详细解释。
行为之一
在FLatentAction
中,最为关键的函数:
virtual void UpdateOperation(FLatentResponse& Response)
{
Response.DoneIf(