UE4の勉強記録

UE4の勉強の記録です。個人用です。

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する My AIの改良

f:id:kazuhironagai77:20210207213711p:plain

<前文>

面白いゲームは最初から面白い

アメリカのトランプ支持派は徹底抗戦する道を選択したみたいですね。日本の〇ウムの時の様にテロを仕掛ける位はやりそうです。しかもアメリカのトランプ支持派の数は半端じゃないので、万が一の確率かもしれませんが内戦になるかもしれません。

アニメの話とか、ネットで非常にオープンにしていた弁護士の動画を最近、久しぶりに見たのですが、この後に及んでまだトランプを支持していました。多分、彼のいる州は、銃で黒人を縛り付けておかなければ国体(州体?)が保てないのでしょう。そのためには、嘘と分かっていてもQの陰謀論に乗るしかないのかもしれません。

しかし、大多数のアメリカ人は逆に、Qのような骨董無形の嘘を信じるからコロナですら駆逐出来ないと思っています。その象徴としてマスクを装着するかしないかで逮捕者まで出ています。元々アメリカ人は、カソリックの本部の嘘に我慢できなくて本国を捨ててまで自分の信仰を貫いた人達の子孫なので、絶対に妥協しません。このQの信望者たちを全て殲滅するためには、そのために南部の州の一つや二つが無くなってもお構いなしで戦い続けるでしょう。

つまりアメリカで第二次南北戦争が起きるのは十分あり得る話なのです。更に、もしテキサス州のような強大な州が南部側について独立宣言したら、どっちが勝つのかすら分からなくなります。

日本だって無関係でいられないと思います。自民党(多数のQの信望者や関係者がいる?)が与党の立場で南部連合を正統なアメリカであると表明したら、北軍の司令官である現大統領が在日米軍を出撃させて東京の国会議事堂を爆撃する事態すら起きるかもしれません。勿論、その時には北海道に傀儡の新日本政府を作っているでしょう。

そこまでは行かなくても、内戦で弱体化したアメリカの僅かな援助を頼りに、世界で最も富める核保有国である中国と渡り合わなければならなくなります。

何か、私の話もQの陰謀論と変わらなくなってしまったのでこの辺で止めます。

ここからは、先週話した、念能力をゲームに追加する件について今週考えた事を述べます。

あれから色々検討したのですが、ゲームのコアの部分がつまらないのに、ボリュームを増やしたからと言って面白くなる事はないとの結論になりました。念能力をゲームに追加するとしても今の時点では、ボリュームを増やしただけになりそうだからです。

そこで今回はそのコアについて考察します。

ゲームの面白さには戦略と戦術があると思います。簡単に言えば戦略は準備をしっかりした方が勝つ、で戦術はその場でうまく立ち回った方が勝つです。

今回は戦術面、つまり戦闘の自由度を増やして、選択の結果が勝利に繋がる様なゲームを考えます。

戦闘中の選択ですが、念能力を追加するまでもなく現時点で、

  • 通常攻撃
  • 魔法攻撃
  • アイテム使用
  • 逃げる

の4択が出来るようになっています。

この4択のどれを選ぶかで戦闘の結果に違いが出なければなりません。

それで以下の様に考えました。

  • 通常攻撃
    • 武器を装備する事で上がる。基準となる攻撃
  • 魔法攻撃
    • 魔法で攻撃する。属性があり、通常攻撃×2~1.8の攻撃力になる
  • アイテム使用
    • HPを回復さえる薬草と、MPを回復させる魔法薬がある。
    • 店で購入出来る薬草は、敵のモンスターが与える1回のダメージより低い回復しか出来ない。つまり戦闘中に使用しても意味がない。
    • 戦闘中に使用しても効果がある高い回復力を持つ特別な薬草は、イベントでしか手に入らない。数に限りがある。
  • 逃げる
    • 30%の確率で戦闘から逃げれる

これならば戦術面で工夫する事で戦闘力が高い敵にも対抗する出来ます。更に絶対勝てない相手には「逃げる」の選択も出来ます。戦術の幅が広がります。

これで行く事にします。

それでは今週の勉強を始めます。

<本文>

1.今週の作業

今週は、my AIを作成します。

2.理論

先週のアイデアを以下に示します。

f:id:kazuhironagai77:20210207213951p:plain

何かが足りないと思っていたんですが分かりました。

それは縄張りです。

モンスターなんで、行動形式は単純であるべきなんです。

  • 縄張りに侵入して来たら、追いかけて来て攻撃する。

まずはこれです。これをAIで作成します。

<知覚>

  • 視覚で縄張りの境界を確認します。縄張りの外側に居る時には興味を示しません。境界内に侵入してくるかどうかだけ見ています。

思考>

  • 特に何も考えていません。

<行動>

  • 普段は、縄張りの境界上をパトロールしています。敵が侵入して来たら襲い掛かります。

まずはこれを実装します。そしてこれが完成してから障害物が縄張り内にある場合を考えます。

3.実装

まず、今までのAIがどの様なものだったかを復習します。

3.1 復習

AIControllerの実装です。

f:id:kazuhironagai77:20210207214130p:plain

これだけです。

次はBehavior Treeです。

f:id:kazuhironagai77:20210207214201p:plain

ここに、

  • 視覚から得た情報
  • トロール
  • 侵入者に突撃

などを追加していきます。

3.2 視覚の追加

まずAIControllerに視覚を作成します。

f:id:kazuhironagai77:20210207214237p:plain

f:id:kazuhironagai77:20210207214245p:plain

正しい設定の詳細は覚えていません。後で、Tutorialを見て確認します。

次にAIPerceptionStimuliSourceをThirdPersonCharacterBPに作成します。

f:id:kazuhironagai77:20210207214304p:plain

設定を以下の様にします。

f:id:kazuhironagai77:20210207214322p:plain

こっから先が覚えていません。先々週のブログで確認します。

結構詳しくまとめてありました。

次はAIControllerです。

視覚が使用されているのかの確認のために以下の実装をしました。

f:id:kazuhironagai77:20210207214341p:plain

このポイントはOn Target Perception Update (AIPerception) を使用する事でした。

テストします。

f:id:kazuhironagai77:20210207214438p:plain

効いていますね。

ついでにGamePlay Debuggerも使用してみます。

設定を変更してGamePlay Debuggerが使用出来るようにします。

Project settingを開き以下のように設定を変えます。

f:id:kazuhironagai77:20210207214458p:plain

先週勉強した公式のOnline Learningではこの後にConsoleの箇所を…と言っていましたが、これだけで十分なはずです。
確認します。

f:id:kazuhironagai77:20210207214518p:plain

動いています。

しかも視覚で認識されている事も確認出来ました。

Blackboardに変数を作成して視覚の結果をBehavior Treeに追加しましょう。

f:id:kazuhironagai77:20210207214535p:plain

AIControllerを以下の様に変更します。

f:id:kazuhironagai77:20210207214552p:plain

取りあえずの動作確認用としてBehavior Treeの実装を以下の様に変更しました。

f:id:kazuhironagai77:20210207214609p:plain

Blackboard Based Conditionの設定はいじっていません。

f:id:kazuhironagai77:20210207214636p:plain

あ、名前だけは変える事にします。先週勉強したOnline Learningで疑問文にした方が分かり易いとあったからです。

f:id:kazuhironagai77:20210207214653p:plain

Task、PrintStringTaskは以下の様に実装しました。

f:id:kazuhironagai77:20210207214714p:plain

先週までで学習したTaskの作成で必要な事は、

  • Event Receive Execute AIを使用する事、
  • FinishExecuteを使用する事、
  • FinishExecuteはSuccessで返す事。

です。これらを守って作成しました。

テストします。

f:id:kazuhironagai77:20210207214738p:plain

動いている事は確認出来ました。

今度は、Behavior TreeのDebug機能を使用してみます。

I see youが表示された所で、Pauseボタンを押します。

I see youを表示させたモンスターは目の前の奴なのでDebugの対象をMyAIController_C_2に指定します。

f:id:kazuhironagai77:20210207214803p:plain

以下の様になっていました。

f:id:kazuhironagai77:20210207214820p:plain

Back Intoボタンを押すとPrintStringTaskが呼ばれている事が分かります。

f:id:kazuhironagai77:20210207214836p:plain

視覚が機能している事の確認が出来ました。

3.3 縄張りの作成

色々考えましたが、縄張りを最初から作成するのは難しいので、モンスターの周り、例えば半径150mに近づいたら攻撃してくる事にします。

f:id:kazuhironagai77:20210207214900p:plain

Blackboardに新しく作成した変数です。

f:id:kazuhironagai77:20210207214927p:plain

その変数の値をTrueに変更するためのServiceのIsHeInTerritoryは以下の様に実装しました。

f:id:kazuhironagai77:20210207214944p:plain

Service内の変数の値とBlackboardのIsHeInTerritoryを繋げます。

f:id:kazuhironagai77:20210207215016p:plain

Blackboardの変数、IsHeInTerritoryがTrueかどうかを判別するDecoratorを追加します。

f:id:kazuhironagai77:20210207215146p:plain

設定は以下の通りです。

f:id:kazuhironagai77:20210207215212p:plain

テストします。

f:id:kazuhironagai77:20210207215232p:plain

あれ、追いかけて来ません。

f:id:kazuhironagai77:20210207215247p:plain

視覚で認識はされています。

f:id:kazuhironagai77:20210207215317p:plain

IsHeInTerritoryがFalseを返しています。

色々調べてたら原因が分かりました。

ServiceのEventであるEvent Receive Activation AIが発動していませんでした。

f:id:kazuhironagai77:20210207215346p:plain

ServiceのEventを、試しにEvent Receive Search Start AIに変更したらモンスターが追いかけて来ました。

f:id:kazuhironagai77:20210207215409p:plain

f:id:kazuhironagai77:20210207215430p:plain

良く考えたらServiceの正しい作成方法知りませんでした。

ちょっと勉強します。

3.4 Serviceの正しい作成方法

ググったらこのサイトが詳しく解説していました。

後、UE4のAnswerHubの「BT Services not activating」の質問の解答に以下の解説が載っていました。

今まで気が付かなかったんですが、UE4のAnswerHubってLogInしないと見れなかったんですね。これからはUE4のAnswerHubのスクリーンショットを貼るときは引用元をはっきり書く事にします。

f:id:kazuhironagai77:20210207215501p:plain

UE4Answer Hubの「BT Services not activating」の解答より引用

上記の説明によると、Event Receive Activationはbranchがsuccessを返した時だけ発動するとあります。となると先程の例で呼ばれないのは納得です。

と言うかこれで完全に解決してしまいました。

3.5 トロールの追加

縄張りに侵入してこない限りは、モンスターはその辺を歩き回っています。最初は先々週勉強したTutorialでやったような

f:id:kazuhironagai77:20210207215604p:plain

の様にパトロールする要所を設定する方法にしたかったのですが、この方法を採用すると全てのモンスターに一々、パトロールする要所を設定する必要がある事に気が付きました。それは大変過ぎるので適当な場所に歩いて移動する事にします。

まず適当な場所を見つけるServiceを作成します。

f:id:kazuhironagai77:20210207215627p:plain

作成したServiceを追加して、Blackboardに適切な変数を作成してそれと関連づけます。

f:id:kazuhironagai77:20210207215654p:plain

これで一応、モンスターはその辺を歩き回るはずです。

テストします。

f:id:kazuhironagai77:20210207215718p:plain

近づくと攻撃してくるので遠くから観察しましたが、全てのモンスターはランダムに移動はしています。

ただし2つ問題がありました。

  • 歩かずに走り回っている。
  • どんどん別な場所に移動してしまう。

これらも直していきます。

3.6 モンスターを歩かせる

これは先々週勉強したtutorialにやり方が出ていました。

f:id:kazuhironagai77:20210207215755p:plain

同じserviceを作成します。

f:id:kazuhironagai77:20210207215813p:plain

Behavior Treeに追加します。

f:id:kazuhironagai77:20210207215834p:plain

Max Walk Speedはぶらぶら歩いている時は150、侵入者を見つけた時は600にセットされるようにしました。

テストします。

遠くからしか観察できないのでスクリーンショットは非常に小さいですが、全部のモンスターがモゴモゴ歩いています。

f:id:kazuhironagai77:20210207215852p:plain

成功したと思ったら、歩いている途中のモンスターに近づいても、モンスターは襲って来ません。

f:id:kazuhironagai77:20210207215909p:plain

あれ。ひょっとして。

Does Monster See Player? の Observer abortsの設定をLower Priorityに変更したら

f:id:kazuhironagai77:20210207215950p:plain

f:id:kazuhironagai77:20210207220003p:plain

モンスターは散歩を中止して凄い勢いで追いかけて来ました。

f:id:kazuhironagai77:20210207220037p:plain

Lower Priorityとはそういう意味だったんですね。

やっと意味が分かりました。

3.7 モンスターを元の位置に戻す。

これもTutorialでやりました。以下にその方法を示します。

f:id:kazuhironagai77:20210207220102p:plain

My AI Controllerから上記の方法と全く同じやり方でやろうとしたら、Monster BPのAIはMy AI Controllerであるにもかかわらず、Get Controlled PawnをMonster BPにcastする事が出来ません。

f:id:kazuhironagai77:20210207220142p:plain

仕方がないので以下の方法でMonsterがspawnする最初の位置をBlackboardにセットしました。

f:id:kazuhironagai77:20210207220213p:plain

更にBehavior Treeを以下の様に改良しました。

f:id:kazuhironagai77:20210207220257p:plain

これで3回ほど適当な場所をうろついたら最初にSpawnした位置に戻ってくれるはずです。

テストします。

f:id:kazuhironagai77:20210207220321p:plain

戻っているみたいです。

Behavior Treeをdebugしたらモンスターは最初の位置に移動しています。

f:id:kazuhironagai77:20210207220347p:plain

一応は出来ました。

4.Lower Priorityの機能の確認

前節の「3.6 モンスターを歩かせる」でLower Priorityの機能の意味が分りました。一応確認のテストを行います。

4.1 テストの条件

まずBehavior Treeを以下の様に組みました。

f:id:kazuhironagai77:20210207220427p:plain

TaskのPrintStringとPrintString3は単にPrintStringを実行するだけです。

f:id:kazuhironagai77:20210207220452p:plain

TaskのPrintString1とPrintString2 はPrintStringを実行する前に3秒間停止します。

f:id:kazuhironagai77:20210207220509p:plain

Blackboardの変数ChooseNumberは1にセットされています。

この条件でテストします。

4.2 Noneの場合

Observer abortsをNoneにセットします。

f:id:kazuhironagai77:20210207220529p:plain

f:id:kazuhironagai77:20210207220536p:plain

ChooseNumberが0に変更された場合でも、実行中のノードPrint String 3は停止しないで最後までtaskの内容を実行しています。その後、変更した条件に沿ったtaskを実行しています。

Behavior treeのdebugも見てみます。

f:id:kazuhironagai77:20210207220611p:plain

ChooseNumberの値が0にセットされました。

f:id:kazuhironagai77:20210207220645p:plain

PrintString3のtaskが終了してSuccessを返しています。

f:id:kazuhironagai77:20210207220709p:plain

その後で、Selectorが新しい条件にあったNodeを選択しています。

4.3 Lower Priorityの場合

今度はObserver abortsをLower Priorityにセットします。

f:id:kazuhironagai77:20210207220734p:plain

テストします。

f:id:kazuhironagai77:20210207220822p:plain

ChooseNumberが0に変更されると、Noneの場合と違い、実行中のノードPrint String 3はただちに中止します。その後、変更した条件に沿ったtaskを実行しています。

Behavior treeのdebugも見てみます。

f:id:kazuhironagai77:20210207220854p:plain

ChooseNumberの値が0にセットされました。

PrintString3の作業は直ちに中止され、新しい条件にあるtaskが実行されています。

f:id:kazuhironagai77:20210207220917p:plain

Abortが発動した時もfailureの信号が親ノードに返されると思っていたのですが、何も返していませんね。

4.4 Lower Priorityまとめ

2021-01-24のブログ

f:id:kazuhironagai77:20210207221008p:plain

と書きましたが、その時は、高い優先順位から低い優先順位に変更された場合でテストしています。

その条件ではLower Priorityは発動しません。

Lower Priorityが発動するのは低い優先順位にあるTaskを実行中に、条件が高い優先順位に変更された場合です。

Observer AbortがNoneにセットされている場合は、低い優先順位にあるTaskを終了してから、変更した条件に合う高い優先順位にあるtaskを実行します。

しかしObserver AbortがLower Priorityにセットされている場合は、低い優先順位にあるTaskをabortして、直ちに変更した条件に合う高い優先順位にあるtaskを実行します。

4.5 2021-01-24のブログのLower Priorityに関しての間違いを直すのか?

直しません。

このブログは滑った転んだの過程を記録するために書き始めたからです。間違えた事も立派な記録です。

2週間前は、Lower Priorityの機能を間違えて理解していましたが、今は正しく理解しています。

この正しい理解に至った過程こそが、大切であると私は思っています。

もし2021-01-24のブログを直してしますと、その正しい理解にたどり着いた過程を消す事になってしまいます。だから2021-01-24のブログは間違った事が書かれていてもそのまま残します。

もっと深い理由もあります。

アメリカに居た時に非常にびっくりした事の一つに、アメリカ社会では、間違いは、絶対に罰しないんです。どんなにその間違いで損害が発生しても、間違った場合は罰しないです。

しかし嘘をついた場合は、ものすごく厳しい罰則があります。

その時に学んだんですが、人間は必ず間違えます。間違えない事は不可能です。しかし間違いは、後で直す事が可能なんです。所が嘘は違います。嘘を突かれると、どこで間違えているのか分からなく成ってしまうんです。その結果、後から直すのがほとんど不可能になります。

だから人は、間違いには寛大で、嘘には厳しく在るべきなんです。

しかし日本では、ほとんどの場合、逆で、間違いには非常に厳しいのに、嘘には寛大です。

これはソフト制作を含めたモノづくりにとって最悪な習慣だと思っています。

だから直しません。

4.6 Lower Priority おまけ

因みに「Unreal Engine 4で極めるゲーム開発」のp414に犬がお母ちゃんと遊んでいる時にお父ちゃんが帰ってきたらの例え話でLower Priorityを説明しています。

Lower Priorityの機能を理解してから読めば、全くその通りの説明ですが、二週間前の私は理解出来ませんでした。

理由は簡単で、私が子供の時に飼っていた犬は、飯を用意してくれるお母ちゃんと遊ぶ方が、お父ちゃんと遊ぶより優先順位が高かったからです。仕事を終えたお父ちゃんが家に帰って来てもその犬はお母ちゃんと遊んでいたらお父ちゃんは無視していました。

だからこの部分を読んだとき、私は勝手にお母ちゃんと遊ぶ方が優先順位が高いと思っていました。

5. EQSの作成

Environment Query Systemなんて仰々しい言い方をしているから、非常に恐れていましたが、数値解析なんかで、面をグリッドで表し、それぞれの点に値を与え、更にinterpolationする時に、点の値をどれくらい考慮するかを重みで表します。要は、これと同じ事をやっているだけだと分かりました。なら恐ろしくないです。

なのでEQSもしっかり勉強する事にします。今まではEQSに関しては読んだだけでしたが今回は実際に作成してみます。

先週、公式のサイトのtutorialがかなり良かったので今回も公式のTutorialで勉強します。(と言うかGoogleで検索したら一番上に出て来たのでそのまま選びました。)このサイトです。

f:id:kazuhironagai77:20210207221131p:plain

今回、私が注意して学習する点は以下の事になります。

  • グリッドの作成方法
  • そのグリッドに対してどうやって値をつけるのか?
  • 重みの計算方法
  • 出来た値と重みをどう使用するのか?

これらの点に注目しながら勉強していきます。

5.1 Environment Query System Quick Start

始める前からアレ何ですが、このTutorial、何時作成されたのかが分かりません。

以下の警告が表示されているんですが、4.26ではEQSは標準で装備されていますので、今もってExperimentalではないとは思います。

f:id:kazuhironagai77:20210207221204p:plain

せめてTutorialが作成された日付が分かると、この時はExperimentalだったんだな。とか今もってExperimentalなのか?とか、学習者が推測できるので有り難いです。

このTutorialで以下のsystemに対しての基本的な理解が出来ると紹介されています。

f:id:kazuhironagai77:20210207221242p:plain

5のEQSと6のEQS Contextsが分けられているのはEQSそのものとEQSの使い方の事でしょうか?興味深いです。

さらに7のAI Debugging Toolsでデバックについても勉強するみたいです。

5.1.1 Required Project Setup

わざわざこのTutorialのために新しいProjectを作成するのも勿体ないので、今あるAIの勉強用のProjectをそのまま使用します。Versionは4.24です。Tutorialは4.21を使用していますが、多分大丈夫でしょう。

Editor PreferenceのEQSにチェックを入れます。

f:id:kazuhironagai77:20210207221307p:plain

どうでも良い話ですが、Editor PreferenceとProject Settings…は違ったんですね。

f:id:kazuhironagai77:20210207221324p:plain

最初、同じものと思ってProject SettingsからEQSを探していました。

以下のBPクラスを作成しました。

f:id:kazuhironagai77:20210207221344p:plain

Characterクラス、AIControllerクラス、Behavior Tree、Blackboardはお馴染みですが、これらとは別に、新たに2つのクラスを作成しました。Environment QueryからとEnvQueryContext_BlueprintBaseからです。

この二つのクラスについての簡単な解説がないか、ググって見たんですが見つからなかったです。

ショウガナイので、先に行きます。

5.1.2 Environment Query Context

早速、EnvQueryContext_BlueprintBaseを使用します。

このクラス何に使用するのか全然分かりません。しかし名前から想像するにEnvironment Queryクラスを使用するためのContextを作成するのかもしれません。

Provide Single Actor関数を以下の様にOverrideしました。

f:id:kazuhironagai77:20210207221405p:plain

Provide Single Actor関数が呼ばれたらPlayerのCharacterを返す(この場合、ThirdPersonCharacterBPを返す)。

それだけの機能です。ここからはまだ何も分かりません。

5.1.3 EQS Setup

今度は、Environment Queryから派生して作成したEQS_FindPlayerにBPで実装していきます。

EQS_FindPlayerを開いて見ると結構寂しいです。

f:id:kazuhironagai77:20210207221440p:plain

RootからPoints:Gridを選択しました。これがGridを作成するのでしょうか?

f:id:kazuhironagai77:20210207221510p:plain

説明が、もろにグリッド作成します。と成ってました。

f:id:kazuhironagai77:20210207221533p:plain

ただQuerierの周りって何処なんでしょうか?それは分かりませんね。

半径と言っているのを見ると、Gridは正方形ではなく円形みたいです。

SimpleGridのPropoertiesの詳細を以下に示します。

f:id:kazuhironagai77:20210207221549p:plain

GridHalfSizeは半径を示しているみたいですね。

SpaceBetweenはグリッド内で値を持つ点同士の距離を示しているみたいです。

Projectionは何なんでしょうか?今はまだ分かりません。

それでは説明文を読んでいきます。

説明文によると、今、作成したSimpleGridや他の選択肢は総じてGeneratorと呼ばれるそうです。そう言えば、

f:id:kazuhironagai77:20210207221621p:plain

と書かれていました。そして何をGenerateするのかと言うとItemだそうです。このItemは後でTestに使用されるそうです。

これだけだと謎が更に深まりますね。

そしたらTestの例が説明されていました。

f:id:kazuhironagai77:20210207221658p:plain

この文章から推測すると、

  • Context Yが先程、EnvQueryContext_BlueprintBaseから派生したクラスでOverirideしたProvide Single Actor関数が返す、GameCharacterの事
  • Itemはグリットのそれぞれの点
  • Testはそのそれぞれの点が持つ値の計算方法を指す

と思われます。

Generate Aroundについての簡潔な説明がありました。

f:id:kazuhironagai77:20210207221734p:plain

どこにグリッドを作成するかを決定するそうです。

f:id:kazuhironagai77:20210207221805p:plain

敵のモンスターを中心にグリッドは作成するので、AI Characterは分かるのですが、querierは何なんでしょうか?

f:id:kazuhironagai77:20210207224551p:plain

だそうです。

因みに、他の選択肢は何があるのか見てみたら、以下のクラス(item?)が表示されました。
f:id:kazuhironagai77:20210207221826p:plain

EnvQueryContext_itemはItemつまり、単なるActorの周りに作成、EQC_PlayerContextはPlayerの周りに作成するのでしょうか?

今度は、SimpleGridにtestを追加します。

f:id:kazuhironagai77:20210207221855p:plain

TraceはQuerierからItemが見えるかどうかと言う事でしょうか?Distanceは単にItemとQuerierとの距離を表していますね。

Traceの設定を以下の様に変更しました。

f:id:kazuhironagai77:20210207221910p:plain

Test PurposeのFilter OnlyはQuerierが見えないitemは全部排除すると言う意味だと思います。前見たEQSの動画でそう説明していた気がします。

ContextにEQC_PlayerContextを選択しましたが、ここが意味が分かりません。EQC_PlayerContextは単純にPlayerの操作するキャラを指していると推測していたのですが違うようです。

Bool MatchはTutorialで解説されていました。

f:id:kazuhironagai77:20210207221941p:plain

だそうです。

Distanceの方は以下の様に設定しました。

これって先週見たOnline LearningのEQSと同じ事やっているんでしょうか?

何か見覚えがあります。

f:id:kazuhironagai77:20210207221959p:plain

先週のTutorialの記憶が正しければ、

Test PurposeのScore Onlyは点数をそれぞれのItemに付ける事で、Scoring Factorに-1を入れると、点数が反対になるはずです。

ここではDistance toでEnv_QueryContext_Querierを選択しています。

5.1.4 Blackboard and Behavior Tree Setup

ここはEQSと関係ないのでスキップします。

5.1.5 Behavior Tree: Patrol Setup

新しいtaskの作成中にGetRandomReachablePointInRadius関数を使用していました。

f:id:kazuhironagai77:20210207222036p:plain

あ。

Reachableじゃない時はfalseを返すみたいです。

「3.5 パトロールの追加」で作成したservice、FindRandomPlaceでGetRandomReachablePointInRadius関数を使用しましたが、使用方法が間違っていました。

直します。

f:id:kazuhironagai77:20210207222053p:plain

Branchを追加してGetRandomReachablePointInRadius関数がFalseを返した場合にも対応できるようにしました。

一応、テストもしてみます。

f:id:kazuhironagai77:20210207222117p:plain

普通に動いています。

GetRandomReachablePointInRadius関数の返した値が、Reachableじゃない時はfalseを返すのでは無くて、GetRandomReachablePointInRadius関数の返した値は全てReachableですが、何らかの理由で値が返せない時に、falseが返されるみたいです。

上記の実装の場合、どちらの解釈が正しくても正常に作動するので、GetRandomReachablePointInRadius関数を使用する場合はこれからもこの方法で実装します。

5.1.6 Behavior Tree: In Combat Setup

Run EQSQuery を使用しました。

f:id:kazuhironagai77:20210207222149p:plain

このTaskは先程、Environment Queryから作成したEQS_FindPlayerを呼び出します。

設定方法は以下の通りでした。

f:id:kazuhironagai77:20210207222207p:plain

Blackboard KeyにBlackboardにある変数を選びます。これは他の Taskと全く同じです。

次にQuery Templateに使用したいEnvironment Queryから作成したクラスを選択します。今回は先程作成したEQS_FindPlayerをセットします。

Run ModeにSingle Best Itemを選択します。これは最も成績が良いitemを一個選ぶ設定です。

5.1.7 AI Controller Setup

ここはかなり複雑なコードを実装していますが、EQSとは関係ないのでスキップします。

5.1.8 Final Setup

EQSとは関係ないのでスキップします。

5.1.9 テストします

Run EQS Queryが実行されています。

f:id:kazuhironagai77:20210207222244p:plain

Failureを返しています。

f:id:kazuhironagai77:20210207222328p:plain

何処かにバグがあるって事ですね。

うーん。

次の章が、このTutorialの最後の章ですが、EQSのテスト方法が紹介されています。これを勉強してDebugに使用出来ないか試してみます。

それでもダメな場合は、先週勉強したOnline learningにEQSのdebug方法が紹介されていたはずなので、それを復習して試してみます。

5.1.10 End Result

GamePlay DebuggerでEQSを見る方法が簡単に紹介されていました。

f:id:kazuhironagai77:20210207222359p:plain

それぞれの点の計算はされているみたいです。

EQS Testing Pawnの使用方法について説明されていました。

まずEQS_PlayerContextのProvide Actors Setを以下の様にOverrideします。

f:id:kazuhironagai77:20210207222419p:plain

作成したEQS Testing PawnのEQSのQuery TemplateにEQS_FindPlayerをセットします。

f:id:kazuhironagai77:20210207222434p:plain

Level上に配置します。

f:id:kazuhironagai77:20210207222451p:plain

Traceが効いているのが分かります。更にDistanceの値がBP_Enemyに近い程高く、遠い程低くなっています。

5.1.11 EQS Testing Pawnいろいろ

折角なんで、EQS Testing Pawnで遊んでみます。

ThirdPersonCharacterBPにEQS Testing Pawnを重ねて置いて見ました。更に障害物は全てどかしました。

f:id:kazuhironagai77:20210207222514p:plain

ThirdPersonCharacterBPに最も近い点が1.00で最も遠い点が0.00と表示されています。

今度はThirdPersonCharacterBPの前に障害物を置いて見ます。

f:id:kazuhironagai77:20210207222536p:plain

障害物の後ろにある点ではTraceが0にセットされました。

EQS Testing Pawn を使用すればEQS_FindPlayerがどんな機能を持っているのかは確認出来ますね。

5.1.12 EQS Quick Start 勉強まとめ(仮)

バグがまだ直っていませんが、忘れないうちにまとめ(仮)を書いておきます。

  • EnvQueryContext_BlueprintBaseがどうやって呼ばれてどのように使用されているのかが分からないです。
  • 色々なTestの設定で、EnvQueryContext_itemEnvQueryContext_QuerierそしてEQC_PlayerContextから選択しましたが、それぞれが何を指しているのか不明。
  • GamePlay DebuggerEQSで、Divideが選択出来ない。

以上が不明な点です。

これらは後でもう一度検討します。

5.2 EQSのDebugの復習

Creating the First EQS QueryからEQS Gameplay Debuggerまでをサラッと見直しました。それだけでも新たに分かった事が結構あったので、まずそれらをまとめます。

<疑問1> GamePlay DebuggerEQSで、Divideが選択出来ない。

Project Setting->GamePlay Debugger->Add Onsを見たらDivideの設定がNum/になっています。

f:id:kazuhironagai77:20210207222645p:plain

試してみます。

Num/を押すとそれぞれのItemの値が表示されました。

f:id:kazuhironagai77:20210207222703p:plain

もう一度押すとそれぞれのItemの値は消えました。

f:id:kazuhironagai77:20210207222809p:plain

<疑問2> EnvQueryContext_BlueprintBaseがどうやって呼ばれてどのように使用されているのかが分からないです。

これは分かってしまえば笑い話ですが、EQC_PlayerContextとしてEnvironment QueryのTestから呼ばれていました。

<疑問3> EnvQueryContext_itemEnvQueryContext_Querierについて

これは実際にテストして確認してみます。

まず、Environment Queryから新しいクラスを派生します。Distanceと名付けます。

f:id:kazuhironagai77:20210207222856p:plain

Distanceの中身は以下に示した様に、Generator にSimpleGridを選択し、TestにDistanceを選択しただけです。

f:id:kazuhironagai77:20210207222918p:plain

Test、DistanceのDistance to をEnvQueryContext_Querierに指定します。

f:id:kazuhironagai77:20210207222933p:plain

EQS_TestPawnから以下のクラスを作成します。名前はMyEQS_TestPawnとしました。

f:id:kazuhironagai77:20210207222951p:plain

EQSのQuery Templateに先程作成したDistanceをセットします。

f:id:kazuhironagai77:20210207223006p:plain

MyEQS_TestPawnをLevel上に配置します。

f:id:kazuhironagai77:20210207223023p:plain

MyEQS_TestPawnから近いItemほど数値が低く、遠いItemほど数値が高くなっています。と言う事は、EnvQueryContext_QuerierはMyEQS_TestPawnを指しています。

つまり実際の場合は、EnvQueryContext_Querierは、そのEQS Queryを呼び出したAIをセットしているCharacterを指していると考えられます。

では、EnvQueryContext_Itemの場合はどうでしょうか?

f:id:kazuhironagai77:20210207223039p:plain

これは全部、0になってしまいました。多分Itemを指定しないといけないのだと思います。後で検討します。

では、EnvQueryContext_BlueprintBaseから自作したEQC_PlayerContextの場合はどうでしょうか?

EQC_PlayerContextでは以下の二つの関数をOverrideしました。

f:id:kazuhironagai77:20210207223055p:plain

f:id:kazuhironagai77:20210207223103p:plain

f:id:kazuhironagai77:20210207223110p:plain

どちらの関数が呼ばれるのかは不明ですが、(多分Overrideしているので両方呼ばれる。)どちらにしてもThirdPersonCharacterBPに対してのDistanceが計算されるはずです。

f:id:kazuhironagai77:20210207223129p:plain

その通りでした。

ここで注意しないといけないのは、Gridとその中のItemはEnvironment Queryを呼び出したAIをセットしているCharacterを中心に作成されている事です。EnvQueryContext_BlueprintBaseはあくまでも計算するための起点の指定です。

*追記

Gridの発生場所は、Simple GridのGenerate Aroundで指定します。「5.1.3 EQS Setup」を読み直していたら自分でそう書いていました。

f:id:kazuhironagai77:20210207223207p:plain

<>その他の役立ちそうな情報

Step to Debug Drawの数値はどのTestまでを実行するか指定出来ます。

f:id:kazuhironagai77:20210207223241p:plain

Querying Modeは色々な結果を表示します。一番条件にあったitem一個だけの表示なども出来ます。

f:id:kazuhironagai77:20210207223318p:plain

5.3 EQSのバグの直し

これだけEQSの知識があれば先程のバグも直せるでしょう。やってみます。

「5.1.10 End Result」で作成したEQS Testing PawnのStep to Debug Drawを1にセットしてみます。

f:id:kazuhironagai77:20210207223342p:plain

以下に示した様に、EQS_FindPlayerの最初のtestであるTranceのContextはEQC_PlayerContextなのでThirdPersonCharacterBPから見て見えるGrid内のitemが選択されます。

f:id:kazuhironagai77:20210207223401p:plain

f:id:kazuhironagai77:20210207223409p:plain

されています。

次にStep to Debug Drawを2にセットしてDistanceを表示させました。

f:id:kazuhironagai77:20210207223433p:plain

Distance toがEnvQueryContext_QuerierにセットされているのでEmenyに一番近い場所が選択されています。

f:id:kazuhironagai77:20210207223449p:plain

これってPlayerの操作するキャラを追いかける目的ならば、EQC_PlayerContextにしないといけませんよね。

確認します。

サイトスクリーンショット

f:id:kazuhironagai77:20210207223506p:plain

EnvQueryContext_Querierにセットされていますが、その下の説明文に

f:id:kazuhironagai77:20210207223521p:plain

となっています。なので

f:id:kazuhironagai77:20210207223549p:plain

に直しました。

f:id:kazuhironagai77:20210207223609p:plain

今度は、Playerの操作するキャラから見えて、更に最も近いItemが選ばれています。

これでテストしてみます。

f:id:kazuhironagai77:20210207223626p:plain

今度はSuccessで返っています。

ただ、この設定だと、EQS_FindPlayerは最初のtestであるTranceが全部outな場合はfailureを返しても良い気もします。つまりFailureで返したとしてもバグとは言えない場合もあると思います。

のでこれ以上は追及するのは止めます。

6.まとめと感想

やっとUE4のAIの勉強が終わりました。大体の基礎は理解したと思います。EQSも大体は理解しました。また個人的な意見ですが、EQSは知覚、思考、行動の分類だと思考に入り、TaskではなくServiceで作成出来るべきだったのではないのかと思いました。

やっとですが、来週からはSaveの機能を改善します。