0、需求

假设我们在做一款偷菜游戏,推出了一系列花里胡哨的农社与作物皮肤作为氪金点。玩家扮演隔壁老王在其他玩家的田地中偷菜,玩家可能会有很多,但可能会有许多人使用最新推出的皮肤,故需要实现一套动态关卡加载与资源管理,使得切换过程中尽可能丝滑无感。

1、动态加载Level

首先给出答案,然后面向答案编程

// 设置一个流关卡显示与隐藏
void ULevelStreaming::SetShouldBeVisible(const bool bInShouldBeVisible)

想要显示或隐藏流关卡,需要先有一个流关卡

ULevelStreamingDynamic* ULevelStreamingDynamic::LoadLevelInstanceBySoftObjectPtr(   
    UObject* WorldContextObject, // 当前的关卡World
    const TSoftObjectPtr<UWorld> Level, // 要加载的关卡的软引用
    const FVector Location, 
    const FRotator Rotation,// 关卡在主关卡中的偏移和旋转 
    bool& bOutSuccess, // 是否找到了这个关卡并成功加载
    const FString& OptionalLevelNameOverride// 不晓得做啥的,可空
)

ULevelStreamingDynamic是流关卡类型,继承自ULevelStreaming,因此也可以使用ULevelStreaming::SetShouldBeVisible。

2、非阻塞加载

使用上面的接口后会发现,这个函数会阻塞主线程的执行,体验并不好,因此需要以异步的方式提前加载场景资源。

// 这个接口默认执行异步加载,可在项目设置中调整
TSharedPtr<FStreamableHandle> UAssetManager::LoadAssetList(
    const TArray<FSoftObjectPath>& AssetList, // 地图路径
    FStreamableDelegate DelegateToCall,       // 完成的回调
    TAsyncLoadPriority Priority,              
    const FString& DebugName
)

除了地图路径以外,其他参数都可空

实际调用如下

TSharedPtr<FStreamableHandle> streamHandle = 
UAssetManager::Get().LoadAssetList(
    TArray<FSoftObjectPath>{Level.ToSoftObjectPath()}, 
    onComplate
);

onComplate为加载完成时的调用,可以在其中按照第一节的步骤创建ULevelStreamingDynamic对象并设置显隐。

3、资源释放

资源管理一般是由UAssetManager决定,理论上有GC在不需要关心这部分。

void FStreamableManager::Unload(const FSoftObjectPath& Target)

通过FStreamableHandle的GetOwningManager()接口拿到对应的Manager并传入资源对应路径即可,但会导致其他引用到这里的资源失效,故十分不建议这么做。