《UE4游戏开发》之 《资源异步加载进度条处理》

  1. 以TArray数组作为容器,搜集所有需要异步加载的资源【assetList】
  2. 加载进度的总体思路:把数组里的数据,按每次步长分成若干份,每完成一次步长,就更新一次UI,并开始下一步长,依次类推,进度条即可实现;
  3. 如何把数组按步长分若干份:这个ue4给TArray提供了一个方法:Append,可以这样写:
//assetList搜集需要动态加载的资源
//loadingIndex新一步长开始的索引
//Count:计算从Ptr中插入的元素的数量
TArray<FSoftObjectPath> list;
list.Append(assetList.GetData() + loadingIndex, Count);
  1. 理解UE4 TArray Append的实现:
/**
	 * Adds a raw array of elements to the end of the TArray.
	 *
	 * @param Ptr   A pointer to an array of elements to add.
	 * @param Count The number of elements to insert from Ptr.
	 * @see Add, Insert
	 */
	void Append(const ElementType* Ptr, SizeType Count)
	{
		check(Ptr != nullptr || Count == 0);
		//根据传进来的参数,分配内存不会调用构造函数
		SizeType Pos = AddUninitialized(Count);
		//根据地址和大小,把含有原数据的内存复制到新分配的内存中
		ConstructItems<ElementType>(GetData() + Pos, Ptr, Count);
	}
	/**
	 * Adds a given number of uninitialized elements into the array.
	 *
	 * Caution, AddUninitialized() will create elements without calling
	 * the constructor and this is not appropriate for element types that
	 * require a constructor to function properly.
	 *
	 * @param Count Number of elements to add.
	 * @returns Number of elements in array before addition.
	 */
	FORCEINLINE SizeType AddUninitialized(SizeType Count = 1)
	{
		CheckInvariants();
		checkSlow(Count >= 0);

		const SizeType OldNum = ArrayNum;
		if ((ArrayNum += Count) > ArrayMax)
		{
			ResizeGrow(OldNum);
		}
		return OldNum;
	}
	FORCENOINLINE void ResizeGrow(SizeType OldNum)
	{
		ArrayMax = AllocatorInstance.CalculateSlackGrow(ArrayNum, ArrayMax, sizeof(ElementType));
		AllocatorInstance.ResizeAllocation(OldNum, ArrayMax, sizeof(ElementType));
	}
	void ResizeAllocation(
			SizeType PreviousNumElements,
			SizeType NumElements,
			SIZE_T NumBytesPerElement
			)
		{
			// Avoid calling FMemory::Realloc( nullptr, 0 ) as ANSI C mandates returning a valid pointer which is not what we want.
			if (Data || NumElements)
			{
				//checkSlow(((uint64)NumElements*(uint64)ElementTypeInfo.GetSize() < (uint64)INT_MAX));
				Data = (FScriptContainerElement*)FMemory::Realloc( Data, NumElements*NumBytesPerElement, Alignment );
			}
		}
		template <typename DestinationElementType, typename SourceElementType, typename SizeType>
	FORCEINLINE void ConstructItems(void* Dest, const SourceElementType* Source, SizeType Count)
	{
		if constexpr (TIsBitwiseConstructible<DestinationElementType, SourceElementType>::Value)
		{
			FMemory::Memcpy(Dest, Source, sizeof(SourceElementType) * Count);
		}
		else
		{
			while (Count)
			{
				new (Dest) DestinationElementType(*Source);
				++(DestinationElementType*&)Dest;
				++Source;
				--Count;
			}
		}
	}
  1. 根据上面的分析,分段已经搞定;再结合UE4 使用UAssetManager进行资源的异步加载,很容易完成这个步长资源的加载,不同的是,我们如何正确循环,直至资源加载完成,并且进度正常表现
  2. 为了能复用,需要用一个结构,把这个关系管理起来,而这个结构需要管理的内容有:
    根据Append分析,需要管理下一个步长开始的索引值:LoadingIndex;
    需要暂存动态加载的资源:AssetList
    需要存储,每一个步长的长度:LoadCountEveryTime
    需要存储,管理进度UI显示的对象:LoadHandle【中间对象,负责转送UI进度事件,如果是网络游戏,需要把每个人的进度同步到各个端去,所以这里需要给服务器同步进度,待服务器确定,并返回结果后,更新进度UI】
  3. 样例代码:
struct ACLoadAsset
{
	ACLoadAsset(TArray<FSoftObjectPath>&& list, TSharedPtr<FLoadHandle> handle, int32 loadCountEveryTime = 100);

	TSharedPtr<FLoadHandle> loadHandle;

	void asyncLoadAsset();

private:

	void onLoadComplate();

private:
	int32 loadingIndex = 0;
	TArray<FSoftObjectPath> assetList;
	int32 m_loadCountEveryTime = 0;
};
ACLoadAsset::ACLoadAsset(TArray<FSoftObjectPath>&& list, TSharedPtr<FLoadHandle> handle, int32 loadCountEveryTime)
	:loadHandle(handle)
	, assetList(MoveTemp(list))
	, m_loadCountEveryTime(loadCountEveryTime)
{

}

void ACLoadAsset::asyncLoadAsset()
{
	if (loadingIndex >= assetList.Num())
	{
		if (!loadHandle->isComplate)
		{
			ACLOG_Info("ACLoadAsset", "加载中asset完毕, 共 {0} 个", assetList.Num());
			loadHandle->isComplate = true;
			loadHandle->onComplate.ExecuteIfBound();
		}

		return;
	}

	int32 endIndex = FMath::Min(loadingIndex + m_loadCountEveryTime, assetList.Num());
	int32 size = endIndex - loadingIndex;
	TArray<FSoftObjectPath> list;
	list.Append(assetList.GetData() + loadingIndex, size);
	loadingIndex += size;

	FStreamableDelegate comdel;
	comdel.BindSP(AsShared(), &ACLoadAsset::onLoadComplate);
	UAssetManager& AssetManager = UAssetManager::Get();
	auto hd = AssetManager.LoadAssetList(
		list,
		comdel
	);

	if (hd && hd->HasLoadCompleted())
	{
		this->onLoadComplate();
	}
}

void ACLoadAsset::onLoadComplate()
{
	float count = (float)assetList.Num();
	loadHandle->onUpdata.ExecuteIfBound(loadingIndex / count);
	this->asyncLoadAsset();
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是用UE4 C++编写异步加载关卡的基本步骤: 1. 创建一个继承自AActor的C++类,这个类将被用来加载关卡。 2. 在.h文件中,添加一个函数声明,用于异步加载关卡。函数原型为:`void LoadLevelAsync(const FString& LevelName);` 3. 在.cpp文件中,实现`LoadLevelAsync`函数。首先,使用`FStreamableManager`类的`LoadLevelAsync`函数来异步加载关卡。这个函数需要两个参数:要加载的关卡名称和一个回调函数,在关卡加载完成后自动调用。 4. 在回调函数中,将加载的关卡传递给一个由你创建的函数,该函数将关卡添加到世界中。 5. 最后,在你的游戏中,调用`LoadLevelAsync`函数来异步加载关卡。 下面是一个简单的代码示例: ```cpp // MyLevelLoader.h #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "MyLevelLoader.generated.h" UCLASS() class MYGAME_API AMyLevelLoader : public AActor { GENERATED_BODY() public: AMyLevelLoader(); UFUNCTION(BlueprintCallable, Category = "Level Loading") void LoadLevelAsync(const FString& LevelName); private: void OnLevelLoaded(ULevel* LoadedLevel); FStreamableManager LevelLoader; }; // MyLevelLoader.cpp #include "MyLevelLoader.h" AMyLevelLoader::AMyLevelLoader() { PrimaryActorTick.bCanEverTick = false; } void AMyLevelLoader::LoadLevelAsync(const FString& LevelName) { LevelLoader.LoadLevelAsync(LevelName, FStreamableDelegate::CreateUObject(this, &AMyLevelLoader::OnLevelLoaded)); } void AMyLevelLoader::OnLevelLoaded(ULevel* LoadedLevel) { if (LoadedLevel) { UWorld* World = GetWorld(); if (World) { World->AddLevelToWorld(LoadedLevel); } } } ``` 在你的游戏中,可以创建AMyLevelLoader实例并调用`LoadLevelAsync`来异步加载关卡。例如: ```cpp AMyLevelLoader* LevelLoader = GetWorld()->SpawnActor<AMyLevelLoader>(); LevelLoader->LoadLevelAsync("MyLevel"); ``` 这里,我们异步加载了名为"MyLevel"的关卡。当关卡加载完成时,`OnLevelLoaded`函数会自动被调用,并将关卡添加到世界中。 希望这可以帮助你开始编写异步加载关卡的代码!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值