一、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";