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并传入资源对应路径即可,但会导致其他引用到这里的资源失效,故十分不建议这么做。