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並傳入資源對應路徑即可,但會導致其他引用到這裏的資源失效,故十分不建議這麼做。