概要
UnrealEngine4 の Environment Query System(EQS)をC++から直接実行するためのメモです。
Behavior Tree は使用しません。
参考
以下の記事を参考にいたしました、ありがとうございます。
公式:Environment Query System ユーザー ガイド
UE4 Document@com04 EQS
AIで色々悩んでみた:EQS編
環境
Windows10
Visual Studio 2017
UnrealEngine 4.26.2
関連ソース
Engine\Source\Runtime\AIModule\Classes\EnvironmentQuery\EnvQueryManager.h
Engine\Source\Runtime\AIModule\Classes\EnvironmentQuery\EnvQueryTypes.h
Engine\Source\Runtime\AIModule\Classes\EnvironmentQuery\EnvQuery.h
準備
現時点で公式マニュアルでは「実験的」となっていますが、エンジンプラグインとしてデフォルトで使用できるようです。
環境クエリを作る
コンテンツブラウザから右クリック -> AI -> 環境クエリ で作成します。
TestingPawn 経由でエディタで見るとこんな感じです。
モジュールの設定
C++で扱うためにモジュールの追加設定をします。
GameplayTasks
とAIModule
の追加が必要です。
PublicDependencyModuleNames.AddRange(
new string[] {
(省略)
"GameplayTasks", // ←追加
"AIModule", // ←追加
});
関連クラス/データ型
UEnvQuery
環境クエリのデータ型。UDataAsset
から継承されています。
FEnvQueryRequest
環境クエリを実行するためのラッパー。
これを経由して実行をします。ジェネレータへ渡すパラメータもこれを経由します。
FEnvQueryResult
結果を受け取るためのデータ型。ジェネレートしたアクター(アイテム)の情報がソートされて保持される模様。
C++からのEQS呼び出し
アクターコード
該当アクターに環境クエリデータを持たせます。アクターに参照を保持してUEエディターで設定をします。
次に実行用メソッドと結果受け取り用のデリゲートオブジェクトを用意します。
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "EnvironmentQuery/EnvQueryTypes.h"
#include "EnvironmentQuery/EnvQuery.h"
class UEnvQuery;
UCLASS(config=Game)
class AMyCharacter : public ACharacter
{
GENERATED_BODY()
// ...省略...
public:
// 環境クエリ
UPROPERTY(EditAnywhere, Category = "AI", meta = (ToolTip="環境クエリ"))
UEnvQuery* MyEnvQuery = nullptr;
// クエリ実行
UFUNCTION(BlueprintCallable, meta = (ToolTip="クエリ実行メソッド"))
void ExecuteMyEnvQuery();
// 結果受け取り
void MyEnvQueryResult(TSharedPtr<FEnvQueryResult> _Result);
};
#include "MyCharacter.h"
#include "EnvironmentQuery/EnvQueryManager.h"
// クエリ実行
void AMyCharacter::ExecuteMyEnvQuery()
{
FEnvQueryRequest _MyQueryRequest = FEnvQueryRequest(MyEnvQuery, this);
// パラメータセット(ジェネレーターの該当パラメータをQueryParamsでバインド)
_MyQueryRequest.SetFloatParam(FName(TEXT("SimpleGrid.GridSize")), 500.0f);
_MyQueryRequest.SetFloatParam(FName(TEXT("SimpleGrid.SpaceBetween")), 200.0f);
_MyQueryRequest.Execute(EEnvQueryRunMode::SingleResult, this, &AMyCharacter::MyEnvQueryResult);
}
// 結果受け取り
void AMyCharacter::MyEnvQueryResult(TSharedPtr<FEnvQueryResult> _Result)
{
if (_Result->IsSuccsessful()) {
// リザルトの中身を表示
const int32 _ItemNum = _Result->Items.Num();
for(int32 _Lp=0;_Lp<_ItemNum;_Lp++){
FVector _Loc = _Result->GetItemAsLocation(_Lp);
float _Score = _Result->GetItemScore(_Lp);
UE_LOG(LogTemp, Log, TEXT("%d, %.2f, (%.2f, %.2f)"), _Lp, _Score, _Loc.X, _Loc.Y );
}
}
}
クエリ実行時のパラメータは BehaviorTree で使用可能なものと同様に SingleResult
RandomBest5Pct
RandomBest25Pct
AllMatching
とありますが、それとは関係なく、FEnvQueryResult
で受け取った結果はスコア順でソートされているようです。
UEエディタ設定
アクターのパラメータに作成した環境クエリを設定し、実行用メソッド ExecuteMyEnvQuery
の呼び出しコードを用意します。(適当にキーボードから呼び出し)
実行結果
FEnvQueryResult
で受け取ったデータが表示されます。
実行時に EEnvQueryRunMode::SingleResult
で結果を1つで指定していますが、中身はソートされてすべて持っているようです。
LogTemp: 0, 1.00, (-751.00, -299.00)
LogTemp: 1, 1.00, (-751.00, 101.00)
LogTemp: 2, 1.00, (-551.00, -499.00)
LogTemp: 3, 1.00, (-551.00, 301.00)
LogTemp: 4, 1.00, (-151.00, -499.00)
...省略
LogTemp: 32, 0.00, (249.00, -99.00)
LogTemp: 33, 0.00, (249.00, 101.00)
LogTemp: 34, 0.00, (249.00, 301.00)
LogTemp: 35, 0.00, (249.00, 501.00)
パラメータの受け渡し
クエリ実行時に以下のようにパラメータを渡しています。
_MyQueryRequest.SetFloatParam(FName(TEXT("SimpleGrid.GridSize")), 500.0f);
_MyQueryRequest.SetFloatParam(FName(TEXT("SimpleGrid.SpaceBetween")), 200.0f);
受け取るためには実行するEQSのジェネレータの DataBinding
をQueryParams
に変更し、パラメータ名を正しく設定する必要があります。
まとめ
公式ページの「ネイティブ コードで EQS を使用する」を元に試してみた結果です。
EQSはAIとセットになっていますが、実際はAIに限らずに使うことができるため利用範囲が広がるのではないかと思います。