UE几种异步加载

UE多线程分为3种。

1.AsyneTask

2.TaskGrop

3. FRunnable

.前两种应该是属于异步 

 1.

.h

#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "Async/AsyncWork.h"
#include "MyAsyncAction.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyAsyncTaskNodeResult, int, Result);
/**
 *
 */
UCLASS()
class EDITORTOOLSDEMO_API UMyAsyncAction : public UBlueprintAsyncActionBase
{
	GENERATED_BODY()

public:

	UPROPERTY(BlueprintReadWrite)
		bool bSelfRun = false;

	UPROPERTY(BlueprintAssignable)
		FMyAsyncTaskNodeResult OnSuccess;


	UPROPERTY(BlueprintAssignable)
		FMyAsyncTaskNodeResult OnFail;

	/// <summary>
	/// 自动销毁的FAutoDeleteAsyncTask
	/// </summary>
	UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
		static UMyAsyncAction* AsyncTaskLoad(int MaxNumber);

	/// <summary>
	/// 手动销毁的FAsyncTask,目前没有实现手动删除。还是建议使用自动删除的AsyncTaskLoad
	/// </summary>
	UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
		static UMyAsyncAction* SimpleAsyncTaskLoad(int MaxNumber);

	void OnTaskFinish();
};


/// <summary>
/// FAutoDeleteAsyncTask:这同样是一个类模板,特点是Task执行完成之后无需手动垃圾回收  https://www.bilibili.com/read/cv9488877/
/// </summary>
class MyAsyncTask : FNonAbandonableTask
{
	/// <summary>
	/// 1.必备
	/// </summary>
	friend class FAutoDeleteAsyncTask<MyAsyncTask>;


	/// <summary>
	/// 自定义传值格式
	/// </summary>
	UMyAsyncAction* Node;
	//当前计数
	int32 CurrentCount = 0;
	//总计数
	int32 TotalCount;

	MyAsyncTask(UMyAsyncAction* waitNode, int Target)
		:Node(waitNode), TotalCount(Target) {}

	~MyAsyncTask()
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("ClearAsyncTask"));
	}


	/// <summary>
	/// 2.必备。 多线程工作函数
	/// </summary>
	void DoWork();

	/// <summary>
	/// 3.必备。根据不同名字修改"MyAsyncTask"
	/// </summary>
	/// <returns></returns>
	FORCEINLINE TStatId GetStatId() const
	{
		RETURN_QUICK_DECLARE_CYCLE_STAT(MyAsyncTask, STATGROUP_ThreadPoolAsyncTasks);
	}


};

/// <summary>
/// FAsyncTask:这是一个类模板,特点是Task初始化之后会被加入到线程池中执行,但是执行结束之后需要手动进行垃圾回收 https://www.bilibili.com/read/cv9488877/ 
/// </summary>
class  SimpleAsyncTask :FNonAbandonableTask
{
	//如 MyAsyncTask 同注释
	friend class FAsyncTask<SimpleAsyncTask>;

	UMyAsyncAction* Node;
	//当前计数
	int32 CurrentCount = 0;
	//总计数
	int32 TotalCount;

public:
	// 构造函数
	SimpleAsyncTask(UMyAsyncAction* waitNode, int Target)
		: Node(waitNode), TotalCount(Target) {}
	// 析构函数
	~SimpleAsyncTask()
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, TEXT("ClearSimpleAsyncTask"));
	}

	// 具体任务逻辑执行
	void DoWork();


	// 固定写法,本类将作为函数参数
	FORCEINLINE TStatId GetStatId() const
	{
		RETURN_QUICK_DECLARE_CYCLE_STAT(SimpleAsyncTask, STATGROUP_ThreadPoolAsyncTasks);
	}
};
.cpp

#include "AsyncLoad/MyAsyncAction.h"

UMyAsyncAction* UMyAsyncAction::AsyncTaskLoad(int MaxNumber)
{
	UMyAsyncAction* Node = NewObject<UMyAsyncAction>();


	///FAutoDeleteAsyncTask:这同样是一个类模板,特点是Task执行完成之后无需手动垃圾回收 
	auto MyTDeleteTask = new FAutoDeleteAsyncTask<MyAsyncTask>(Node, MaxNumber);
	MyTDeleteTask->StartBackgroundTask();

	return Node;

}

FAsyncTask<SimpleAsyncTask>* MyTask;
UMyAsyncAction* UMyAsyncAction::SimpleAsyncTaskLoad(int MaxNumber)
{
	UMyAsyncAction* Node = NewObject<UMyAsyncAction>();


	/// <summary>
	/// FAsyncTask:这是一个类模板,特点是Task初始化之后会被加入到线程池中执行,但是执行结束之后需要手动进行垃圾回收
	/// </summary>

	MyTask = new FAsyncTask<SimpleAsyncTask>(Node, MaxNumber);
	MyTask->StartBackgroundTask();



	 MyTask->StartSynchronousTask; 在当前线程执行,可能会导致主线程阻塞S
	//GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("END111"));

	/* MyTask->IsDone()  可以配合定时器检测是否完成任务

	等待任务完成后,进行手动删除 [下面这段会导致卡顿。 目前没有实现手动删除。还是建议使用自动删除]
	/*MyTask->EnsureCompletion();
	delete MyTask;
	MyTask = nullptr;*/

	return Node;
}


//注意,需要在等待FAsyncTask结束后手动回收,如果FAsyncTask需要一段时间才能结束,可能需要在“Timer”中循环执行下面函数
void UMyAsyncAction::OnTaskFinish()
{
	//使用IsDone()判断task是否完成
	bool bTaskFinished = MyTask->IsDone();
	if (bTaskFinished)
	{
		//手动回收内存
		delete MyTask;
		MyTask = nullptr;
	}
}

void MyAsyncTask::DoWork()
{


	while (CurrentCount < TotalCount)
	{
		/// <summary>
	   /// 判断是否在游戏进程中
	   /// </summary>
		AsyncTask(ENamedThreads::GameThread, [=]()
			{

				//Node->OnSuccess.Broadcast(Actor->Counter);
				CurrentCount++;
				Node->OnSuccess.Broadcast(CurrentCount);
				GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, FString::Printf(TEXT("this is %d"), CurrentCount), true);

			}
		);

		FPlatformProcess::Sleep(0.1f);
	}


	AsyncTask(ENamedThreads::GameThread, [=]()
		{
			Node->OnFail.Broadcast(-1);
		}
	);

	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("ENDAsyncTask"));

	/// <summary>
	/// 跳出就是结束多线程
	/// </summary>
	return;
}

void SimpleAsyncTask::DoWork()
{
	while (CurrentCount < TotalCount)
	{
		/// <summary>
	   /// 判断是否在游戏进程中
	   /// </summary>
		AsyncTask(ENamedThreads::GameThread, [=]()
			{

				//Node->OnSuccess.Broadcast(Actor->Counter);
				CurrentCount++;
				Node->OnSuccess.Broadcast(CurrentCount);
				GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, FString::Printf(TEXT("this is %d"), CurrentCount), true);
				Node->OnTaskFinish();

			}
		);

		FPlatformProcess::Sleep(0.1f);
	}


	AsyncTask(ENamedThreads::GameThread, [=]()
		{
			Node->OnFail.Broadcast(-1);
		}
	);
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("ENDSimpleAsyncTask"));

	/// <summary>
	/// 跳出就是结束多线程
	/// </summary>
	return;
}

2.

.h


#pragma once

#include "CoreMinimal.h"
#include "Engine/AssetManager.h"
#include "MyAssetManager.generated.h"


DECLARE_DYNAMIC_DELEGATE(FOnAsyncLoadFinished);
/**
 *
 */
UCLASS()
class EDITORTOOLSDEMO_API UMyAssetManager : public UAssetManager
{
	GENERATED_BODY()

		UPROPERTY()
		FString LoadPackagePath;

	bool bIsLoaded;

	UFUNCTION(BlueprintCallable, Category = "AsyncLoadLevelTest")
		void MyAsyncLoad(FSoftObjectPath path, const FOnAsyncLoadFinished& OnAsyncLoadFinished);

	UFUNCTION(BlueprintCallable, Category = "AsyncLoadLevelTest")
		float GetLoadProgress();

};
.cpp



#include "AsyncLoad/MyAssetManager.h"




void UMyAssetManager::MyAsyncLoad(FSoftObjectPath path, const FOnAsyncLoadFinished& OnAsyncLoadFinished)
{

	//LoadPackagePath = FPaths::GetBaseFilename(, LoadPackageSoftObjectPath.ToString(), false);
	LoadPackagePath = path.ToString();
	bIsLoaded = false;

	UE_LOG(LogTemp, Warning, TEXT("String: %s"), *LoadPackagePath);
	LoadPackageAsync(
		LoadPackagePath,
		FLoadPackageAsyncDelegate::CreateLambda([=](const FName& PackageName, UPackage* LoadedPackage, EAsyncLoadingResult::Type Result)
			{
				if (Result == EAsyncLoadingResult::Failed)
				{
					UE_LOG(LogTemp, Warning, TEXT("Load Failed"));
				}
				else if (Result == EAsyncLoadingResult::Succeeded)
				{
					bIsLoaded = true;
					UE_LOG(LogTemp, Warning, TEXT("Load Succeeded"));
					OnAsyncLoadFinished.ExecuteIfBound();
				}
			}), 0, PKG_ContainsMap);

}

float UMyAssetManager::GetLoadProgress()
{
	float FloatPercentage = GetAsyncLoadPercentage(*LoadPackagePath);
	if (!bIsLoaded)
	{
		FString ResultStr = FString::Printf(TEXT("Percentage: %f"), FloatPercentage);
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, ResultStr);
		UE_LOG(LogTemp, Warning, TEXT("Percentage: %f"), FloatPercentage);
	}
	else {
		FloatPercentage = 100;
	}
	return FloatPercentage;

}

3.


#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Runtime/Core/Public/Async/TaskGraphInterfaces.h"
#include "AsyncLoadActor.generated.h"

/// <summary>
/// TaskGraph 系统是UE4一套抽象的异步任务处理系统可以创建任意多线程任务, 异步任务, 序列任务, 并行任务等,并可以指定任务顺序, 设置任务间的依赖, 最终形成一个任务图 https://zhuanlan.zhihu.com/p/430187330
/// 特点是操作简单,可以用来进行一些简单的运算,但是复杂运算请使用FRunnable.  https://www.bilibili.com/read/cv9486649?spm_id_from=333.999.0.0 
/// </summary>
class FTaskGraphTest
{


public:
	FTaskGraphTest(int32 TotalCount) :TotalCount(TotalCount) {}
	~FTaskGraphTest() {}

	//最大计数
	int32 TotalCount;
	//初始计数
	int32 CurrentCount = 0;


	// 固定写法
	FORCEINLINE TStatId GetStatId() const
	{
		//第一个参数传入自己创建的TaskGraph类型,第二个参数照写即可 
		RETURN_QUICK_DECLARE_CYCLE_STAT(FTaskGraph_SimpleTask, STATGROUP_TaskGraphTasks);
	}

	// 指定在哪个线程运行 ,该函数返回一个目标线程类型,决定taskgraph运行之后在哪个线程执行.
	static ENamedThreads::Type GetDesiredThread()
	{
		/*
		 *在这里我们返回AnyNormalThreadNormalTask(优先级正常的线程)
		 *额外说明:
		 *1.可选线程有GameThread(主线程),AnyThread(任意线程),AudioThread(音频线程)等等
		 *2.线程逻辑在GameThread执行,可能会阻塞主线程,不可取
		 *3.线程逻辑很明显也不适合在音频线程,渲染线程之类的专职线程上进行
		 *4.AnyThread,字如其名,如果把线程逻辑放到AnyThread线程上执行,那这段线程逻辑有可能会
		 *被随机分配一个线程上执行,运气不好的话,分配渲染,物理,音频线程上执行就会出问题
		 *5.所以我们选择了对AnyThread进行限制,即:
		 *AnyNormalThreadNormalTask = AnyThread | NormalThreadPriority | NormalTaskPriority
		 *具有正常线程优先级和正常任务优先级的线程,这样就会避免线程逻辑运行在错误的线程上
		*/
		return ENamedThreads::AnyNormalThreadNormalTask;
	}

	// 后续执行模式 固定写法
	static ESubsequentsMode::Type GetSubsequentsMode()
	{

		return ESubsequentsMode::TrackSubsequents;
	}

	// 线程逻辑执行函数
	void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
	{
		//本例在task中打印从0-TotalCount的数值,每次打印间隔1s
		while (CurrentCount < TotalCount)
		{
			UE_LOG(LogTemp, Warning, TEXT("Current Count :%d"), CurrentCount);
			CurrentCount++;
			FPlatformProcess::Sleep(1.f);
		}
	}
};




/**
 *FRunnable多线程 https://zhuanlan.zhihu.com/p/430187330   & https://blog.csdn.net/u011718663/article/details/118105259#_14
 */
class EDITORTOOLSDEMO_API FSimpleRunnable : public FRunnable
{
public:
	FSimpleRunnable(const FString& ThreadName);
	~FSimpleRunnable();


	void PauseThread();				// 线程挂起 方法一	
	void WakeUpThread();			// 线程唤醒 方法一
	void Suspend(bool bSuspend);	// 线程挂起/唤醒 方法二
	void StopThread();				// 停止线程,一般用该方法
	void ShutDown(bool bShouldWait);// 停止线程,bShouldWait true的时候可强制 kill 线程

private:

	FString m_ThreadName;
	int32 m_ThreadID;
	bool bRun = true;				// 线程循环标志
	bool bPause = false;			//线程挂起标志
	FRunnableThread* ThreadIns;		// 线程实例
	FEvent* ThreadEvent;			//FEvent指针,挂起/激活线程, 在各自的线程内使用


	/// <summary>
	/// FRunnable 4个固有函数
	/// </summary>
	virtual bool Init() override;
	/// <summary>
	/// 执行函数
	/// </summary>
	virtual uint32 Run() override;
	virtual void Stop() override;
	virtual void Exit() override;
};


DECLARE_DYNAMIC_DELEGATE(FOnAsyncLoadLevel);
DECLARE_DYNAMIC_DELEGATE(FOnAsyncLoadFailed);

UCLASS()
class EDITORTOOLSDEMO_API AAsyncLoadActor : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	AAsyncLoadActor();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	/// <summary>
	/// TaskGraph生成
	/// </summary>
	UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
		void CreateTaskGraphSimpleTask(int32 TotalCount);


	/// <summary>
	/// 异步加载关卡 LoadingScreen https://blog.csdn.net/ttod/article/details/127716475 &https://www.bilibili.com/video/BV1Mr4y1A7nZ/?spm_id_from=333.337.search-card.all.click&vd_source=8a96d7f1981e32d11ac0eb4847916c44
	/// </summary>
	bool bIsLoaded;

	/// <summary>
	/// 加载包地址
	/// </summary>
	UPROPERTY(BlueprintReadWrite)
		FString LoadPackagePath;

	//UPROPERTY(EditAnywhere, BlueprintReadWrite)
	//	FSoftObjectPath LoadPackageSoftObjectPath;


	/// <summary>
	/// 生成异步加载关卡绑定回调
	/// </summary>
	UFUNCTION(BlueprintCallable, Category = "AsyncLoadLevelTest")
		void MyAsyncLoad(FSoftObjectPath path, const FOnAsyncLoadLevel& OnAsyncLoadFinished, const FOnAsyncLoadFailed& OnAsyncLoadFailed);


	/// <summary>
	/// 异步加载关卡进度
	/// </summary>
	UFUNCTION(BlueprintCallable, Category = "AsyncLoadLevelTest")
		float GetLoadProgress();



	/// <summary>
	/// FRunnable
	/// </summary>
protected:
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

private:
	static FSimpleRunnable* MySimpleRunnable; // 声明静态单例

public:

	/// <summary>
	/// FRunnable线程生成
	/// </summary>
	UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
		void CreateNewThread(const FString& ThreadName);

	/// <summary>
	/// FRunnable线程挂起
	/// </summary>
	UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
		void PauseThread();

	/// <summary>
	/// FRunnable程挂起/唤醒 方法二
	/// </summary>
	UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
		void SuspendThread(bool bSuspend);

	/// <summary>
	/// FRunnable线程唤醒
	/// </summary>
	UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
		void WakeUpThread();

	/// <summary>
	///  FRunnable停止
	/// </summary>
	UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
		void StopThread();

	/// <summary>
	///  FRunnable强制停止(有概率引起崩溃。建议:使用另一个停止)
	/// </summary>
	UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
		void ForceKillThread(bool bShouldWait);


};

FSimpleRunnable* AAsyncLoadActor::MySimpleRunnable = nullptr;
.cpp

#include "AsyncLoad/AsyncLoadActor.h"


// Sets default values
AAsyncLoadActor::AAsyncLoadActor()
{
	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AAsyncLoadActor::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void AAsyncLoadActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

void AAsyncLoadActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{

	if (MySimpleRunnable) // 防止线程挂起,退出无响应
	{
		MySimpleRunnable->StopThread();
		delete MySimpleRunnable;
		MySimpleRunnable = nullptr;
	}
}

void AAsyncLoadActor::CreateNewThread(const FString& ThreadName)
{
	if (MySimpleRunnable == nullptr)
	{
		MySimpleRunnable = new FSimpleRunnable(ThreadName);
	}
}

void AAsyncLoadActor::PauseThread()
{
	if (MySimpleRunnable)
	{
		MySimpleRunnable->PauseThread();
	}
}

void AAsyncLoadActor::SuspendThread(bool bSuspend)
{
	if (MySimpleRunnable)
	{
		MySimpleRunnable->Suspend(bSuspend);
	}
}

void AAsyncLoadActor::WakeUpThread()
{
	if (MySimpleRunnable)
	{
		MySimpleRunnable->WakeUpThread();
	}
}

void AAsyncLoadActor::StopThread()
{
	if (MySimpleRunnable)
	{
		MySimpleRunnable->StopThread();
	}
}

void AAsyncLoadActor::ForceKillThread(bool bShouldWait)
{
	if (MySimpleRunnable)
	{
		MySimpleRunnable->ShutDown(bShouldWait);
		delete MySimpleRunnable;
		MySimpleRunnable = nullptr;
	}

}

FSimpleRunnable::FSimpleRunnable(const FString& ThreadName)
{
	// 获取 FEvent 指针
	ThreadEvent = FPlatformProcess::GetSynchEventFromPool();

	// 创建线程实例
	m_ThreadName = ThreadName;
	ThreadIns = FRunnableThread::Create(this, *m_ThreadName, 0, TPri_Normal);
	m_ThreadID = ThreadIns->GetThreadID();
	UE_LOG(LogTemp, Warning, TEXT("Thread Start! ThreadID = %d"), m_ThreadID);
}

FSimpleRunnable::~FSimpleRunnable()
{
	if (ThreadEvent)	// 清空 FEvent*
	{
		FPlatformProcess::ReturnSynchEventToPool(ThreadEvent); // delete ThreadEvent;
		ThreadEvent = nullptr;
	}
	if (ThreadIns)		// 清空 FRunnableThread*
	{
		delete ThreadIns;
		ThreadIns = nullptr;
	}
}

void FSimpleRunnable::PauseThread()
{
	bPause = true;
	UE_LOG(LogTemp, Warning, TEXT("Thread Pause!"));
}

void FSimpleRunnable::WakeUpThread()
{
	bRun = true;
	bPause = false;
	if (ThreadEvent)
	{
		ThreadEvent->Trigger(); // 唤醒线程
	}
	UE_LOG(LogTemp, Warning, TEXT("Thread Wakeup!"));
}

void FSimpleRunnable::Suspend(bool bSuspend)
{
	if (ThreadIns)
	{
		ThreadIns->Suspend(bSuspend); //挂起/唤醒
	}
}

void FSimpleRunnable::StopThread()
{
	Stop();
}

void FSimpleRunnable::ShutDown(bool bShouldWait)
{
	if (ThreadIns)
	{
		ThreadIns->Suspend(false);
		ThreadIns->Kill(bShouldWait); // bShouldWait 为false,Suspend(true)时,会崩
	}
}

bool FSimpleRunnable::Init()
{
	return true;//若返回 false ,线程创建失败,不会执行后续函数
}

uint32 FSimpleRunnable::Run()
{

	int32 count = 0;
	FPlatformProcess::Sleep(0.03f); //延时,等待初始化完成
	while (bRun)
	{
		if (bPause)
		{
			ThreadEvent->Wait(); // 线程挂起
			if (!bRun)			 // 线程挂起时执行线程结束
			{
				return 0;
			}
		}

		UE_LOG(LogTemp, Warning, TEXT("ThreadID: %d, Count: %d"), m_ThreadID, count);
		count++;
		FPlatformProcess::Sleep(0.1f); // 执行间隔,防止堵塞
	}

	return 0;
}

void FSimpleRunnable::Stop()
{

	bRun = false;
	bPause = false;
	if (ThreadEvent)
	{
		ThreadEvent->Trigger(); // 保证线程不挂起
	}

	GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, FString::Printf(TEXT("Stop")), true);

}

void FSimpleRunnable::Exit()
{
	GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, FString::Printf(TEXT("Exit")), true);
}



/// <summary>
/// TaskGraph
/// </summary>
void AAsyncLoadActor::CreateTaskGraphSimpleTask(int32 TotalCount)
{

	/*
   *第一个参数默认值为NULL
   *第二个参数为线程逻辑运行的线程,上面已经介绍过
   *ConstructAndDIspatchWhenReady会调用创建的TaskGraph类型(本例中为FTaskGraph)的构造函数,只需要按照构造
   *函数参数列表传入参数即可完成TaskGraph的初始化
  */
	TGraphTask<FTaskGraphTest>::CreateTask().ConstructAndDispatchWhenReady(TotalCount);
}



/// </summary>
/// <param name="path">地址</param>
/// <param name="OnAsyncLoadFinished">成功回调</param>
/// <param name="OnAsyncLoadFaile">失败回调</param>
void AAsyncLoadActor::MyAsyncLoad(FSoftObjectPath path, const FOnAsyncLoadLevel& OnAsyncLoadFinished, const FOnAsyncLoadFailed& OnAsyncLoadFaile)
{
	//LoadPackagePath = FPaths::GetBaseFilename(LoadPackageSoftObjectPath.ToString(), false);
	LoadPackagePath = FPaths::GetBaseFilename(path.ToString(), false);

	bIsLoaded = false;

	UE_LOG(LogTemp, Warning, TEXT("String: %s"), *LoadPackagePath);
	//UE_LOG(LogTemp, Warning, TEXT("String2: %s"), *LoadPackagePath2);
	LoadPackageAsync(
		LoadPackagePath,
		FLoadPackageAsyncDelegate::CreateLambda([=](const FName& PackageName, UPackage* LoadedPackage, EAsyncLoadingResult::Type Result)
			{
				if (Result == EAsyncLoadingResult::Failed)
				{
					UE_LOG(LogTemp, Warning, TEXT("Load Failed"));
					OnAsyncLoadFaile.ExecuteIfBound();
				}
				else if (Result == EAsyncLoadingResult::Succeeded)
				{
					bIsLoaded = true;
					UE_LOG(LogTemp, Warning, TEXT("Load Succeeded"));
					OnAsyncLoadFinished.ExecuteIfBound();
				}
			}), 0, PKG_ContainsMap);
}

float AAsyncLoadActor::GetLoadProgress()
{
	/// <summary>
	/// 加载资产进度
	/// </summary>
	/// <returns>资产路径</returns>
	float FloatPercentage = GetAsyncLoadPercentage(*LoadPackagePath);
	if (!bIsLoaded)
	{
		FString ResultStr = FString::Printf(TEXT("Percentage: %f"), FloatPercentage);
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, ResultStr);
		UE_LOG(LogTemp, Warning, TEXT("Percentage: %f"), FloatPercentage);
	}
	else {
		FloatPercentage = 100;
	}
	return FloatPercentage;
}

4.

.h


#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "AsyncLoadFunctionLibrary.generated.h"


DECLARE_DYNAMIC_DELEGATE_OneParam(FAsyncTaskDelegate, int, IntOut);
/**
 * https://zhuanlan.zhihu.com/p/430187330
 */
UCLASS()
class EDITORTOOLSDEMO_API UAsyncLoadFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()

		/// <summary>
		/// 使用AsyncTask 
		/// </summary>
		UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
		static void AsyncPrimeCount(FAsyncTaskDelegate Out, int32 PrimeCount);



};
.cpp

#include "AsyncLoad/AsyncLoadFunctionLibrary.h"

void UAsyncLoadFunctionLibrary::AsyncPrimeCount(FAsyncTaskDelegate Out, int32 PrimeCount)
{
	// Schedule a thread                               // Pass in our parameters to the lambda expression
	AsyncTask(ENamedThreads::AnyHiPriThreadNormalTask, [Out, PrimeCount]()
		{

			//整个代码只是循环数字并计算它找到了多少质数
			int PrimesFound = 0;
			int TestNumber = 2;

			/*	while (PrimesFound< PrimeCount)
				{
					TestNumber++;
					Out.ExecuteIfBound(TestNumber);

					/// <summary>
					/// 等待0.1秒再执行下一次
					/// </summary>
					FPlatformProcess::Sleep(0.1f);
				}
				return;*/
			while (PrimesFound < PrimeCount)
			{
				bool bIsPrime = true;

				for (int i = 2; i < TestNumber / 2; i++)
				{
					if (TestNumber % i == 0)
					{
						bIsPrime = false;
						break;
					}
				}

				if (bIsPrime)
				{
					PrimesFound++;

					// 每1000个质数

					if (PrimesFound % 1000 == 0)
					{
						//回到主线程并传递参数
						AsyncTask(ENamedThreads::GameThread, [Out, PrimesFound]()
							{
								// 执行委托和参数
								Out.ExecuteIfBound(PrimesFound);
							});
					}
				}
				TestNumber++;
			}
		});
}

引用[1]:案例测试: //同步加载单个资源LoadSynchronous FSoftObjectPath Path =FString(TEXT("Texture2D'/Game/StarterContent/Textures/T_Brick_Clay_Beveled_D.T_Brick_Clay_Beveled_D'")); UTexture2D* Image = UAssetManager::GetStreamableManager().LoadSynchronous<UTexture2D>(Path,false,nullptr); if (Image) { UE_LOG(LogTemp,Warning,TEXT("Image is %s"),*Image->GetName()); } 。 引用[2]:案例测试: //同步加载资源组 TArray<FSoftObjectPath> Paths; Paths.AddUnique(FString(TEXT("Texture2D'/Game/StarterContent/Textures/T_Brick_Clay_Beveled_N.T_Brick_Clay_Beveled_N'"))); Paths.AddUnique(FString(TEXT("Texture2D'/Game/StarterContent/Textures/T_Brick_Clay_New_D.T_Brick_Clay_New_D'"))); //注意:在资源未完成加载之前代码会在这一行暂停运行以等待资源加载完成。 TSharedPtr<FStreamableHandle> SyncStreamableHandle3 = UAssetManager::GetStreamableManager().RequestSyncLoad(Paths); if (SyncStreamableHandle3) { TArray<UObject*>LoadedAssets; SyncStreamableHandle3->GetLoadedAssets(LoadedAssets); if (LoadedAssets.Num() > 0) { for (int32 i = 0; i < LoadedAssets.Num(); i ) { UTexture2D* Image3 = Cast<UTexture2D>(LoadedAssets[i]); if (Image3) { UE_LOG(LogTemp, Warning, TEXT("Image3 is %s"), *Image3->GetName()); } } } } 。 引用[3]:测试案例介绍: FSoftObjectPath Path2 = FString(TEXT("Texture2D'/Game/StarterContent/Textures/T_Brick_Clay_Beveled_M.T_Brick_Clay_Beveled_M'")); //注意:在资源未完成加载之前代码会在这一行暂停运行以等待资源加载完成。 TSharedPtr<FStreamableHandle> SyncStreamableHandle2 = UAssetManager::GetStreamableManager().RequestSyncLoad(Path2); if (SyncStreamableHandle2) { //使用RequestSyncLoad(方法加载单个资源的时候要用GetLoadedAsset来获得FStreamableHandle中返回的资源。 UTexture2D* Image2 = Cast<UTexture2D>(SyncStreamableHandle2->GetLoadedAsset()); if (Image2) { UE_LOG(LogTemp, Warning, TEXT("Image2 is %s"), *Image2->GetName()); } } 。 问题:UE异步加载image 回答: 在提供的案例中,使用了同步加载的方式来加载UE中的图像资源。通过调用UAssetManager的LoadSynchronous方法,可以同步加载单个资源。例如,在案例[1]中,通过指定资源的路径,可以加载指定的图像资源。如果加载成功,可以通过Image指针来访问图像资源的属性,如名称。类似地,在案例[2]和案例[3]中,也展示了如何同步加载资源组和单个资源。通过使用RequestSyncLoad方法和GetLoadedAsset方法,可以加载并访问相应的图像资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值