虚幻引擎UE4 Http之异步请求数据

Posted 飞起的猪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了虚幻引擎UE4 Http之异步请求数据相关的知识,希望对你有一定的参考价值。

一、BlueprintAsyncActionBase

  UE提供了BlueprintAsyncActionBase类,实现异步加载的方式请求数据

虚幻的很多蓝图节点都采用了异步加载的方式

比如:延迟Delay,PlayMontage都是采用异步加载的方式进行实现

接下我们就用异步加载的方式实现HTTP请求下载

二、HTTP请求

新建一个C++项目,然后build.cs加入模块

		PrivateDependencyModuleNames.AddRange(new string[]  "HTTP", "Json" );

 创建继承自BlueprintAsyncActionBase的C++类

请求的Json格式的数据为

 代理声明:代理参数跟我们后面函数的输出参数有关,要想输出函数参数,需要在代理的位置声明返回参数

DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FRequestCompleteDelegate, const TArray<FNewsItem>&, Data,int32, ver);

 

 输出节点如OnSucceeded,OnFailed这些节点跟我们声明的代理有关,就是代理声明的对象

//输出节点
	UPROPERTY(BlueprintAssignable)
		FRequestCompleteDelegate OnSucceeded;

	//输出节点
	UPROPERTY(BlueprintAssignable)
		FRequestCompleteDelegate OnFailed;

 

 主要代码如下MyBlueprintAsyncActionBase.h文件

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "Interfaces/IHttpRequest.h"
#include "MyBlueprintAsyncActionBase.generated.h"


USTRUCT(BlueprintType)
struct FNewsItem 
	GENERATED_USTRUCT_BODY()

		FNewsItem() 
	FNewsItem(FString _Path, FString _Url)
		: Path(_Path), Url(_Url) 
	UPROPERTY(BlueprintReadOnly)
		FString Path;
	UPROPERTY(BlueprintReadOnly)
		FString Url;
;

DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FRequestCompleteDelegate, const TArray<FNewsItem>&, Data,int32, ver);


/**
 * 
 */
UCLASS()
class TEST111_API UMyBlueprintAsyncActionBase : public UBlueprintAsyncActionBase

	GENERATED_BODY()
	
	UMyBlueprintAsyncActionBase();

	//json
public:

	// 自定义的异步蓝图节点
	UFUNCTION(BlueprintCallable, Category = "AsyncHttp", meta = (BlueprintInternalUseOnly = "true"))
		static UMyBlueprintAsyncActionBase* AsyncHttpURLRequest(const FString& url);

	void HttpRequestStar(const FString& url);
	void HttpRequest_RecHandle(FHttpRequestPtr request, FHttpResponsePtr response, bool bWasSuccessful);

	//输出节点
	UPROPERTY(BlueprintAssignable)
		FRequestCompleteDelegate OnSucceeded;

	//输出节点
	UPROPERTY(BlueprintAssignable)
		FRequestCompleteDelegate OnFailed;


	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Version")
		int32 version;

;

MyBlueprintAsyncActionBase.cpp文件

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyBlueprintAsyncActionBase.h"
#include "HttpModule.h"
#include "Interfaces/IHttpResponse.h"

UMyBlueprintAsyncActionBase::UMyBlueprintAsyncActionBase()

	if (HasAnyFlags(RF_ClassDefaultObject) == false)
	
		AddToRoot(); //防止自动GC
	


UMyBlueprintAsyncActionBase* UMyBlueprintAsyncActionBase::AsyncHttpURLRequest(const FString& url) 
	UMyBlueprintAsyncActionBase* AsyncHttpObject = NewObject<UMyBlueprintAsyncActionBase>();
	AsyncHttpObject->HttpRequestStar(url);
	return AsyncHttpObject;


//开始请求
void UMyBlueprintAsyncActionBase::HttpRequestStar(const FString& url)

	TSharedRef<IHttpRequest, ESPMode::ThreadSafe> request = FHttpModule::Get().CreateRequest();
	request->SetVerb("GET");
	request->SetURL(url);
	//request->SetHeader(TEXT("Content-Type"), TEXT("application/json;charset=utf-8"));
	//request->SetTimeout(200);
	//request->SetContentAsString(requestJsonStr); POST时候用
	//request->OnRequestProgress().BindUObject() 绑定进度
	request->OnProcessRequestComplete().BindUObject(this, &UMyBlueprintAsyncActionBase::HttpRequest_RecHandle); //请求回调
	request->ProcessRequest();

//请求回调
void UMyBlueprintAsyncActionBase::HttpRequest_RecHandle(FHttpRequestPtr request, FHttpResponsePtr response, bool bWasSuccessful) 


	if (bWasSuccessful && response.IsValid() && EHttpResponseCodes::IsOk(response->GetResponseCode())) 
		TSharedPtr<FJsonObject> jsonObj;
		TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(response->GetContentAsString());

		if (FJsonSerializer::Deserialize(JsonReader, jsonObj)) 
			version = jsonObj->GetIntegerField("versionId");
			UE_LOG(LogTemp, Warning, TEXT("version is %d"), version);
			const TArray< TSharedPtr<FJsonValue> >* dataList;
			if (jsonObj->TryGetArrayField(TEXT("DataList"), dataList)) // 获取数组
			
				TArray<FNewsItem> newsList;
				for (TSharedPtr<FJsonValue> item : *dataList)
				
					TSharedPtr<FJsonObject>itemObjet = item->AsObject();

					newsList.Add(FNewsItem(
						itemObjet->GetStringField(TEXT("Path")),
						itemObjet->GetStringField(TEXT("Url"))
					));
				
				OnSucceeded.Broadcast(newsList, version); // 成功的执行
				RemoveFromRoot();
				return;
			
		
	
	OnFailed.Broadcast(,0); // 失败的执行
	RemoveFromRoot();


打开关卡蓝图:

 三、HTTP下载

这里我重新建了一个继承自BlueprintAsyncActionBase的类

TestBlueprintAsyncActionBase.h文件

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "Interfaces/IHttpRequest.h"
#include "TestBlueprintAsyncActionBase.generated.h"


/**
 * 
 */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FDownloadProgressDelegate1,
const int32&, ReceivedDataInBytes,
const int32&, TotalDataInBytes,
const TArray<uint8>&, BinaryData
);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDownloadDelegate1);

UCLASS()
class TEST111_API UTestBlueprintAsyncActionBase : public UBlueprintAsyncActionBase

	GENERATED_BODY()

	UTestBlueprintAsyncActionBase();
public:
	//load data

	UFUNCTION(BlueprintCallable, Category = "MyAsyncHttp", meta = (BlueprintInternalUseOnly = "true"))
		static UTestBlueprintAsyncActionBase* AsyncHttpDownload(const FString& url);

	void DownloadRequestStart(const FString& url);
	void DownloadRequestHandle(FHttpRequestPtr request, FHttpResponsePtr response, bool bWasSuccessful);
	void DownloadProgress(FHttpRequestPtr request, int32 bytesSent, int32 bytesReceived);

	//输出节点
	UPROPERTY(BlueprintAssignable)
		FDownloadProgressDelegate1 OnProgressUpdate; //最好把有参数的放在前面,不然参数在蓝图节点有时候被吃掉了

	//输出节点
	UPROPERTY(BlueprintAssignable)
		FDownloadDelegate1 Load_Ok;

	//输出节点
	UPROPERTY(BlueprintAssignable)
		FDownloadDelegate1 Load_Fail;
;

TestBlueprintAsyncActionBase.cpp文件 

// Fill out your copyright notice in the Description page of Project Settings.


#include "TestBlueprintAsyncActionBase.h"
#include "HttpModule.h"
#include "Interfaces/IHttpResponse.h"

UTestBlueprintAsyncActionBase::UTestBlueprintAsyncActionBase()

	if (HasAnyFlags(RF_ClassDefaultObject) == false)
	
		AddToRoot(); //防止自动GC
	


UTestBlueprintAsyncActionBase* UTestBlueprintAsyncActionBase::AsyncHttpDownload(const FString& url)

	UTestBlueprintAsyncActionBase* AsyncHttpObject = NewObject<UTestBlueprintAsyncActionBase>();
	AsyncHttpObject->DownloadRequestStart(url);
	return AsyncHttpObject;


void UTestBlueprintAsyncActionBase::DownloadRequestStart(const FString& url)

	TSharedRef<IHttpRequest, ESPMode::ThreadSafe> request = FHttpModule::Get().CreateRequest();
	request->SetVerb("GET");
	request->SetURL(url);
	request->OnProcessRequestComplete().BindUObject(this, &UTestBlueprintAsyncActionBase::DownloadRequestHandle); //请求回调
	request->OnRequestProgress().BindUObject(this, &UTestBlueprintAsyncActionBase::DownloadProgress);// 下载进度
	request->ProcessRequest();

	RemoveFromRoot(); // 手动GC


void UTestBlueprintAsyncActionBase::DownloadRequestHandle(FHttpRequestPtr request, FHttpResponsePtr response, bool bWasSuccessful)

	RemoveFromRoot();


	if (bWasSuccessful && response.IsValid() && EHttpResponseCodes::IsOk(response->GetResponseCode()))
	
		if (response->GetContentLength() > 0) 
			TArray<uint8> EmptyData;
			OnProgressUpdate.Broadcast(response->GetContentLength(), response->GetContentLength(), EmptyData);
		

		FString FileSavePath = FPaths::ProjectDir() + "/download/gameplay.png";
		FString Path, Filename, Extension;
		FPaths::Split(FileSavePath, Path, Filename, Extension);
		IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();

		if (!PlatformFile.DirectoryExists(*Path))
		
			if (!PlatformFile.CreateDirectoryTree(*Path))
			
				UE_LOG(LogTemp, Error, TEXT("Create Directory Failed!"));
				Load_Fail.Broadcast();
				return;
			
		

		IFileHandle* FileHandle = PlatformFile.OpenWrite(*FileSavePath);
		if (FileHandle)
		
			FileHandle->Write(response->GetContent().GetData(), response->GetContentLength());
			delete FileHandle;
			Load_Ok.Broadcast();
			return;
		
		else 
			UE_LOG(LogTemp, Error, TEXT("Save File Failed!"));
			Load_Fail.Broadcast();
			return;
		

	
	UE_LOG(LogTemp, Error, TEXT("download File Failed!"));
	Load_Fail.Broadcast();
	return;


void UTestBlueprintAsyncActionBase::DownloadProgress(FHttpRequestPtr request, int32 bytesSent, int32 bytesReceived)

	if (request->GetResponse()->GetContentLength() > 0)
	
		TArray<uint8> EmptyData;
		OnProgressUpdate.Broadcast(bytesReceived, request->GetResponse()->GetContentLength(), EmptyData);
	



打开关卡蓝图设置:

 注意这里图片下载的位置在项目工程里新建了一个download的文件夹里面

FString FileSavePath = FPaths::ProjectDir() + "/download/gameplay.png";

 

 

UE4 异步资源加载

概述

虚幻引擎4中有几个新系统,使得可以更加轻松地异步加载资源数据,这些系统代替了虚幻引擎3中的免搜索内容包中存在的很多功能。这些新方法既可以在开发数据应用也可以在设备上的烘焙数据上进行应用,所以您不必保留两个根据需要加载数据的代码路径。有两种通用的方法可以供您根据需要来引用及加载数据。

FStringAssetReferences 和TAssetPtr

美术师或设计师引用资源的最简单的方法是,创建一个UProperty 强指针,并赋予其一个类目。在虚幻引擎4中,如果您使用强指针UObject 属性引用一个资源,那么当加载包含该属性的对象时将会加载那个资源(通过把对象放置在地图中,或者通过从类似于游戏信息这样的东西中进行引用该对象)。如果您不关心是否在游戏启动时能够100%地加载您的资源。如果您想让 美术师/设计师 使用和强指针一样的用户界面来引用特定的资源,而不是总是加载引用的资源,请使用 FStringAssetReference 或 TAssetPtr 。

FStringAssetReference 是一个简单的结构体,包含了一个具有资源完整名称的字符串。如果您在类中创建一个那种类型的属性,那么它将会显示在编辑器中,就像是个 UObject * 属性一样。它还可以正确地处理烘焙和重定向,所以如果您有一个StringAssetReference,那么它一定可以在设备上正常工作。TAssetPtr基本上就是一个封装了 FStringAssetReference 的 TWeakObjectPtr ,它使用一个特定的类作为模板,以便您能限制编辑器用户界面,使其仅允许选择特定的类。如果所引用的资源存在于内存中,那么 TAssetPtr.Get() 将返回该资源。如果该资源不在内存中,那么您那可以调用 ToStringReference() 来查找它引用的资源,并使用下面介绍的方法加载该资源,然后再次调用TAssetPtr.Get() 解除对该资源的引用。

如果美术师或设计师正在手动地设置引用,那么使用 TAssetPtrs 和 StringAssetReferences是非常适合的,但如果您想进行类似于查询这样的处理,查找满足特定要求的资源,而不是加载所有资源,那么您应该使用资源注册表及对象库。

资源注册表和对象库

资源注册表是一个存储资源的元数据的系统,允许您搜索及查询这些资源。编辑器使用此资源注册表来显示内容浏览器中的信息,但是您也可以从游戏性代码使用该注册表,来查找当前没有加载的游戏资源的相关元数据。要想使得一个资源的数据是可搜索的,您需要给该属性添加"AssetRegistrySearchable"标签。查询资源注册表会返回FAssetData类型的对象,它包含了关于该对象的信息和一个 键-》值 对映射表,该映射表包含了标记为可搜索的属性。

处理成组的未加载的资源的最简单方法是使用 ObjectLibrary 。ObjectLibrary 是一个对象,包含了一系列继承共享基类的未加载对象或者未加载对象的FAssetData 。您可以通过提供一个搜索路径来加载一个对象库,它将加载那个路径中的所有资源。这是非常有用的,因为您可以为不同类型指定您的部分内容文件夹,且 美术师/设计师 不需要手动编辑清单表就可以添加新的资源。这里是一个示例,展示了如何使用一个对象库来从磁盘加载AssetData:

if(!ObjectLibrary){ObjectLibrary=ConstructObject<UObjectLibrary>(UObjectLibrary::StaticClass());ObjectLibrary->ObjectBaseClass=BaseClass;ObjectLibrary->UseWeakReferences(GIsEditor);ObjectLibrary->AddToRoot();}ObjectLibrary->LoadAssetDataFromPath(TEXT("/Game/PathWithAllObjectsOfSameType");if(bFullyLoad){ObjectLibrary->LoadAssetsFromAssetData();}

在这个示例中,它创建了一个新的对象库,关联了一个基类,然后加载了给定路径中的所有资源数据。接着选择性地加载真正的资源。如果资源很小,或者如果您正在进行烘焙且需要确保烘焙所有内容,那么您则需要完全地加载这些资源。只要您在烘焙过程中执行一次资源注册表查询,并加载返回的资源,您的对象库就可以正常地在烘焙数据中发挥作用,就像在开发过程中的处理一样。一旦把资源数据放入到了一个ObjectLibrary 中,您可以进行查询并选择性地加载特定的资源。这里是示例,展示了如何进行查询:

TArray<FAssetData>AssetDatas;ObjectLibrary->GetAssetDataList(AssetDatas);for(int32 i =0; i <AssetDatas.Num();++i){FAssetData&AssetData=AssetDatas[i];constFString*FoundTypeNameString=AssetData.TagsAndValues.Find(GET_MEMBER_NAME_CHECKED(UAssetObject,TypeName));if(FoundTypeNameString&&FoundTypeNameString->Contains(TEXT("FooType"))){returnAssetData;}}

在这个示例中,它搜索对象库并查找任何具有 TypeName 文本域且该文本域中包含 "FooType" 的对象,然后返回第一次查找到的对象。一旦有了此 AssetData ,您可以调用 ToStringReference() 来把它转换成一个 FStringAssetReference ,然后您可以使用下一个系统进行异步加载。

StreamableManager(动态加载管理器)和异步加载

现在,您具有一个引用了磁盘上一个资源的 FStringAssetReference ,那么怎样真正地异步加载它哪? FStreamableManager是完成这个处理的最简单方法。首先,您需要创建一个 FStreamableManager ,我建议你把它放到某个全局游戏单例对象中,比如在 DefaultEngine.ini中使用 GameSingletonClassName 指定的对象。然后,您可以把 StringAssetReference 传给该对象,并启动进行加载。SynchronousLoad将会进行简单的、阻碍加载,并返回该对象。这种方法对于较小的对象可能很好,但是它可能会使您的主线程停顿时间过长。出现那种情况,您将需要使用 RequestAsyncLoad ,它将会异步地加载一组资源,并在完成后调用一个代理。这里是一个示例:

voidUGameCheatManager::GrantItems(){TArray<FStringAssetReference>ItemsToStream;FStreamableManager&Streamable=UGameGlobals::Get().StreamableManager;for(int32 i =0; i <ItemList.Num();++i){ItemsToStream.AddUnique(ItemList[i].ToStringReference());}Streamable.RequestAsyncLoad(ItemsToStream,FStreamableDelegate::CreateUObject(this,&UGameCheatManager::GrantItemsDeferred));}voidUGameCheatManager::GrantItemsDeferred(){for(int32 i =0; i <ItemList.Num();++i){UGameItemData*ItemData=ItemList[i].Get();if(ItemData){MyPC->GrantItem(ItemData);}}}

在这个示例中,ItemList 是一个 TArray< TAssetPtr<UGameItem> > ,由设计人员在编辑器中进行修改。这段代码迭代那个列表,将列表项转换为 StringReferences ,并将它们进行排队。当所有列表项都加载后(或者由于缺失列表项而不能加载),它将在代理中调用已通过的相关处理。该代理然后迭代同样的列表项,区分它们,并把它们提供给一个玩家。StreamableManager 在调用该代理之前,将保持到任何它所加载的对象的强引用,所以在调用该代理之前,您可以确保您想异步加载的任何对象都没有被垃圾回收。当调用该代理后,StreamableManager 释放这些引用,所以您想确保它们一直存在,您需要在其他地方对它们进行强引用。

您可以使用同样的方法来异步加载 FAssetDatas ,仅需在它们上面调用 ToStringReference ,把它们添加到一个数组中,然后使用一个代理调用RequestAsyncLoad 。该代理可以是您想操作的任何内容,所以如果需要,你可以传入负载信息。通过把结合使用上述的方法,您应该可以设置一个高效加载游戏中的任何资源的系统。转换直接访问内存的游戏代码来处理异步加载将会花费一些时间,但是之后您的游戏停顿将变得非常少,并且内存使用量会更低。

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

以上是关于虚幻引擎UE4 Http之异步请求数据的主要内容,如果未能解决你的问题,请参考以下文章

UE4新手引导之下载和安装虚幻4游戏引擎

UE4 异步资源加载

UE4 编译虚幻引擎

ue4 虚幻四引擎项目无法打包,求帮助!

UE4异步操作总结转载

虚幻引擎UE4加载GIS数据《数字孪生&智慧城市》