メモ

CG 関連、ゲームエンジン関連メモ

Unreal Engine HTTP リクエスト / レスポンス処理 (JSON デシリアライズ)

Unreal Engine で HTTP リクエストする方法

モジュールを追加

  • プロジェクトに HTTP , Json モジュールを追加する
    • /Games/<プロジェクト名>/Source/<プロジェクト名>/<プロジェクト名>.Build.cs
    • PublicDependencyModuleNames.AddRange()
      • “HTTP” , “Json” を追加

HTTP リクエストするクラスを作成

クラスを追加する

  • Content Browser > C++ Classes > 右クリック
    • New C++ Class…
    • None 選択 > Next 押下
    • Name を HttpRequest に設定 > Create Class 押下

HttpRequest を実装する

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

#pragma once

#include "CoreMinimal.h"
#include "http.h"

class HTTPREQUESTSAMPLE_API HttpRequest
{
public:
    HttpRequest();
    ~HttpRequest();
    void OnRequest(const FString Url, const FString Verb);

private:
    static void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool ConnectionSuccessfully);
};
// Fill out your copyright notice in the Description page of Project Settings.

#include "HttpRequest.h"

HttpRequest::HttpRequest()
{
}

HttpRequest::~HttpRequest()
{
}

void HttpRequest::OnRequest(const FString Url, const FString Verb)
{
    // HTTP リクエストオブジェクト作成
    const FHttpRequestRef Request = FHttpModule::Get().CreateRequest();
    // HTTP リクエスト完了時に実行する関数ポインタをリセット
    Request->OnProcessRequestComplete().BindStatic(&HttpRequest::OnResponseReceived);

    // HTTP リクエストの URL を設定
    Request->SetURL(Url);
    // HTTP リクエストのメソッドを設定
    Request->SetVerb(Verb);
    // HTTP リクエスト
    Request->ProcessRequest();
}

void HttpRequest::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool ConnectionSuccessfully)
{
    // レスポンスコードのチェック
    if (EHttpResponseCodes::IsOk(Response->GetResponseCode()))
    {
        // JSON オブジェクト格納用変数の初期化
        TSharedPtr<FJsonObject> ResponseObj = MakeShareable(new FJsonObject());
        // 文字列から JSON を読み込むための Reader 初期化
        TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());

        // 文字列から JSON オブジェクト
        if (FJsonSerializer::Deserialize(Reader, ResponseObj))
        {
            // UE でログ出力 (API から受け取った文字列そのまま)
            UE_LOG(LogTemp, Display, TEXT("Response %s"), *Response->GetContentAsString());
            // UE でログ出力 (JSON から title キーの文字列のみ出力)
            UE_LOG(LogTemp, Display, TEXT("Title %s"), *ResponseObj->GetObjectField("slideshow")->GetStringField("title"));
        }
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Reponse Code : %d"), Response->GetResponseCode());
    }
}
  • http リクエストするアクターを作成
    • C++ Classes > 右クリック > Add C++ Class …
    • Actor クラスを親に設定する
  • ヘッダファイル
    • HttpRequest を include する
    • SendRequest メンバ関数を定義する
      • Blueprint から呼び出すため UFUNCTION マクロを指定する
        • BlueprintCallable
        • Category = ”Http”
// ApiTest.h

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

#pragma once

#include "CoreMinimal.h"
#include "HttpRequest.h" // <= include

#include "GameFramework/Actor.h"
#include "ApiTest.generated.h"

UCLASS()
class HTTPREQUESTSAMPLE_API AApiTest : public AActor
{
    GENERATED_BODY()

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

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

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

    UFUNCTION(BlueprintCallable, Category = "Http") // <= メンバ関数定義
    void SendRequest();
};
// ApiTest.cpp

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

#include "ApiTest.h"

// Sets default values
AApiTest::AApiTest()
{
    // 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 AApiTest::BeginPlay()
{
    Super::BeginPlay();

}

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

}

void AApiTest::SendRequest()  // <= メンバ関数の実装
{
    HttpRequest Request;
    Request.OnRequest("https://httpbin.org/json", "GET");
}
  • Blueprint クラスを作成する
    • C++ Classes > <プロジェクト名> > ApiTest > MRB (マウス右クリック)
    • Create Blueprint Class Based on ApiTest
    • 作成した Blueprint クラスを任意のレベルに配置

HTTP リクエストを行う

  • 画面のボタンを押下すると HTTP リクエストを行うようにする

  • Widget Blueprint を追加する

    • Content Browser > Widget Blueprint
    • 適当な名前に変更 > (ex) WBP_ApiRequest

  • Widget Blueprint を編集する (Designer) (画面のボタン追加のため)
    • Canvas Panel を配置
      • Palette > PANEL > Canvas Panel > Hierarchy へドラッグ
      • Palette > COMMON > Button > Hierarchy へドラッグ
      • Palette > COMMON > Text > Hierarchy へドラッグ
    • Button
      • Details > Slot > Anchors > Center
      • Details > Slot > Alignment > 0.5 0.5
    • Text
      • Details > Content > Text > Send Request

  • Widget Blueprint を編集する (Graph) (ボタン押下イベントに、API リクエスト処理を割り当てるため)
    • My Blueprint > VARIABLES > Navigation > Button_0 > 選択
    • Details > Events > On Cliecked > + ボタン押下 > On Clicked Event を追加
    • Get Actor Of Class Blueprint を追加
      • Actor Class > BP_ApiTest を指定
      • Return Value > BP_ApiTest クラスを取得する
    • Send Request を実行する

  • Level Blueprint を編集する
    • Editor > Open Level Blueprint
    • Event BeginPlay 以下に追加
      • Create Widget
        • Class > WBP_ApiRequst を指定
      • Add To Viewport

  • Play
    • Send Request ボタン押下 > Output Log にレスポンス結果表示

クラスの関連の概観 (PlantUML)

@startuml class diagram
' ---------- 
' definition 
' ----------
skinparam BackgroundColor #WhiteSmoke
skinparam PackageStyle rectangle

' ---------- 
' definition 
' ----------

package /Engine/Source/Runtime/Online/HTTP/Public
{
    class FHttpModule
    {
        {static} + Get() : FHttpModule&
        + CreateRequest() : TSharedRef<IHttpRequest, ESPMode::ThreadSafe>
    }

    package Interfaces
    {   
        class IHttpBase
        
        class IHttpResponse
        {
            + GetContentAsString(): FString
        }

        class IHttpRequest
        {
            + SetVerb(const FString& Verb) : void
            + SetURL(const FString& URL) : void 
            + ProcessRequest() : bool
            + OnProcessRequestComplete() : FHttpRequestCompleteDelegate&
        }
        note right of IHttpRequest::OnProcessRequestComplete
            * マクロを利用して Delegate の宣言  
            ** DECLARE_DELEGATE_ThreeParams(
                FHttpRequestCompleteDelegate, 
                FHttpRequestPtr /*Request*/, 
                FHttpResponsePtr /*Response*/, 
                bool /*bConnectedSuccessfully*/)
        end note

    }
}

package /Engine/Source/Runtime/Json/Public
{
    package Dom
    {
        class FJsonObject
    }

    package Serialization
    {
        class TJsonReader{}

        class TJsonReaderFactory
        {
            {static} + Create(FString&& JsonString) : TSharedRef<TJsonReader<TCHAR>>
        }

        class FJsonSerializer
        {
            {static} +Deserialize(const TSharedRef<TJsonReader<CharType>>& Reader, TSharedPtr<FJsonObject>& OutObject, EFlags InOptions = EFlags::None) : bool
        }
    }
}

package C++
{
    class HttpRequest
    {
        + OnRequest(const FString Url, const FString Verb) : void
        {static} - OnResponseRecieved(FHttpRequestPtr Request, FHttpResponsePtr Response, bool ConnectionSuccessfully) : void
    }
    note right of HttpRequest::OnRequest 
    * OnRequest の処理内容
    ** FHttpModule::Get で FHttpModule のインスタンスを取得 (Singleton ライクな実装)
    ** FHttpModule.CreateRequest で IHttpRequet インタフェースを取得 
        (プラットフォームに合わせたインタフェース。SharedReference かつ スレッドセーフ)
    ** IHttpRequest->OnProcessRequestComplete デリゲートに、OnResponseRecieved 関数をバインド
    ** IHttpRequest->SetURL に、問い合わせ URL を設定
    ** IHttpRequest->SetVerb に、HTTP メソッドを設定
    ** IHttpRequest->ProcessRequest でリクエスト実行
    end note

    note right of HttpRequest::OnResponseRecieved
    * OnResponseRecieved の処理内容 (IHttpRequest の OnProcessRequestComplete デリゲートに登録)
    ** FJsonObject (SharedPointer) を作成
    ** TJsonReader (SharedReference) を TJsonReaderFactory::Create で作成 
    ** FJsonSerializer::Desirialize で、FJsonObject と TJsonReader を受け取りデシリアライズ
    ** デシリアライズしたレスポンスをログに表示する
    end note

    class ApiTest
    {
        + SendRequest() : void
    }
}

package Blueprint
{
    class BP_ApiTest

    class WBP_ApiRequest
    note right of WBP_ApiRequest
    * Widget Blueprint Class
    ** Button の Onclicked イベントに BP_ApiTest の SendRequest を紐づけ
    end note
}

' ---------- 
' relation 
' ----------
TJsonReader <-- TJsonReaderFactory : use

IHttpBase <|-- IHttpRequest : extends 
IHttpBase <|-- IHttpResponse : extends

FHttpModule <---- HttpRequest : use
IHttpRequest <---- HttpRequest : use
IHttpResponse <---- HttpRequest : use
TJsonReaderFactory <---- HttpRequest : use
FJsonObject <---- HttpRequest : use
FJsonSerializer <---- HttpRequest : use

HttpRequest <-- ApiTest : use

ApiTest <|-- BP_ApiTest : extends

BP_ApiTest <-- WBP_ApiRequest : use

@enduml

参考

#UnrealEngine5 でHTTPリクエスト機能を実装してAPIコールしてみた | DevelopersIO

https://youtu.be/vLGZp5hl6qU

https://www.youtube.com/watch?v=c6gad7tXfTM

[UE4] HTTP通信 その1 ~基本編~|株式会社ヒストリア

[UE4] HTTP通信 その2 ~HTTPモジュールの構成~|株式会社ヒストリア

HTTP

Json

Unreal スマート ポインタ ライブラリ

デリゲート

httpbin.org

Config に任意の値を設定して Blueprint で読み込む

  • config の格納場所

    • <プロジェクトルート>\Config 配下
  • Config の種類

    • DefaultEditor.ini
    • DefaultEditorPerProjectUserSettings.ini
    • DefaultEngine.ini
    • DefaultGame.ini
    • DefaultInput.ini
  • Config のカテゴリ

    • 全般
      • Engine
      • Game
      • Input
      • DeviceProfiles
      • GameUserSettings
      • Scalability
      • RuntimeOptions
      • InstallBundle
      • Hardware
      • GameplayTags
    • エディタのみ
      • Editor
      • EditorPerProjectUserSettings
      • EditorSettings
      • EditorKeyBindings
      • EditorLayout
    • デスクトップのみ
      • Compat
      • Lightmass

Visual Studio での配置場所

  • Games/<プロジェクト名>/Config 配下

Blueprint から config の任意の値を呼び出す方法

  • 適当なアクターを作成する

    • C++ Classes > 右クリック > Add C++ Class …
    • Actor クラスを親に設定する
  • ヘッダファイル

    • UCLASS に Config のカテゴリを設定する
    • UPROPERTY に Config 、BlueprintReadOnly を指定する
      • このクラスの関連する config 上の値を読み込むようにする
      • Blueprint 上で読み込みができるようにする
// ConfigActor.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ConfigActor.generated.h"

UCLASS(Config = Game) // <= Config のカテゴリを指定する
class HTTPREQUESTSAMPLE_API AConfigActor : public AActor
{
    GENERATED_BODY()

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

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

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

    UPROPERTY(Config, BlueprintReadOnly) // <= Config, Blueprint 読み込み設定
    int Value1;

    UPROPERTY(Config, BlueprintReadOnly) // <= Config, Blueprint 読み込み設定
    FString Value2;
};
  • config に任意の値を追加する (今回は DefaultGame.ini を対象)
    • section ([] で括られた部分) は以下のように指定する
      • /<作成したクラスまでのパス>/<プロジェクト名>.<クラス名>
      • [/Script/HttpRequestSample.ConfigActor]
; DefaultGame.ini

[/Script/EngineSettings.GeneralProjectSettings]
ProjectID=ECD3E9DE4FA3F0986CAD50850CAD6E64
ProjectName=Third Person Game Template

; 任意値の追加
[/Script/HttpRequestSample.ConfigActor]
Value1=1
Value2="test"
  • 作成した C++ クラス (ConfigActor) を継承した Blueprint クラスを生成する

    • Content Browser > C++ Classes >対象 C++ > MRB (右クリック) > Create Blueprint class based on ConfigActor
  • 生成した Blueprint クラスを任意の level に配置

  • Config の任意の値が読み込まれるか確認する

  • Blueprint クラスを編集
    • Event Graph
      • Blueprint の要素表示設定から継承したクラスの変数を表示するように変更
        • (ギアアイコン) > Show Inherited Variable
        • 継承元 Config Actor の変数を表示
      • プレイ時に読み込むようにする
        • Event BeginPlay のピンのあとに、Print String を呼び出す
        • それぞれの Config の値を In String に設定する

- レベルのゲームプレイ設定を変更する - Standalone Game

  • それぞれの値が読み込まれていることを確認
    • Value1 = 1
    • Value2 = test

参考

コンフィギュレーション ファイル

クラス指定子

プロパティ

https://www.youtube.com/watch?v=QQH0y_5NU3Y

[UE4] 「エディタの環境設定」や「プロジェクト設定」に項目を追加する|株式会社ヒストリア

Unreal Engine プロジェクト起動時に自作 C++ 見つからなくなる

自作 C++ クラスから継承した Blueprint を作成後、
プロジェクトを再度起動した際に、
継承元のクラスが消えて見つからなくなった。

Blueprint could not be loaded because it derives from an invalid class.  
Check to make sure the parent class for this blueprint hasn't been removed! 
Do you want to continue (it can crash the editor)?

対応

  • Editor Preferences > General > Loading & Saving
    • Force Compilation at Startup > check
  • プロジェクトの再起動

  • 再起動後、認識されるようになる

参考