《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();
}

猜你喜欢

转载自blog.csdn.net/qq_21919621/article/details/107788148