ぜひ基礎編を開きながらご覧ください。
この記事には、改良版と完全版があります。あわせてご確認ください。
お約束 | |
---|---|
この記事作成にあたって使用した(主な)UE4バージョン | 4.26.2 |
このtoolkitのバージョン | v3.0 (live 12.12.20) |
本日のゴール
敵に接触するとそれ以上動けないようにする
ZOCってなに
いわゆるZOC:Zone Of Controlってやつですね。
概要
これが最善の案なのかはわかりませんが、非常に苦労しました。
①敵の周囲のマスのIndex番号を全てリストアップ
②敵の周囲のマスからスタートする経路検索を全てキャンセル
概要の解説
①はまあわかると思いますが、②の意味が分かりませんね。
このアセットでは、経路検索を下記のようにして実装しているようです。
①経路検索をスタートするマスをParentと呼び、その周囲のマスを全てピックアップ(6角形タイルの場合、最大6マスある。)
②Parentから周囲のマスに移動するコストを調べる。そのマスに移動するためのコストが計算される。
③次にそのタイルをParentとして、周囲のマスを検索する。
とこのように、次々にコストを計算していきます。
<例>
例に従ってやってみると、上のタイルPにPlayerがいるとします。①タイルPをParent(=親)として周囲のマスを検索。タイルAとタイルBがあります。
②移動コストをそれぞれ調べます。タイルAはコスト2、タイルBはコスト1でした。
③次にタイルAをParent = 親として経路検索。タイルPへはコスト2、タイルBへはコスト1、タイルCへはコスト1でした。タイルPからのコストは2だったので、タイルAを経由したルートではP:4、B:3、C:3となります。
④次にタイルBをParent = 親として経路検索。タイルPへはコスト1、タイルAへはコスト1、タイルCへはコスト1でした。タイルPからのコストは1だったので、タイルBを経由したルートではP:2、B:2、C:2となります。
これらのコストのうち、最小のコストが選択されるというわけです。
つまり移動コストは
P:0
A:2
B:1
C:2
となります。Aに行くルートはこの場合P→AでもP→B→Aでも等しくなりますが、どっちが選択されるんでしょう。(わかりません)
そしてこの検索方法をとると、最も効率がいいルートがわかるので、ルート表示が使えるというわけです。
ZOCの実装では、この敵の周囲のマスをParentとするすべてのルートをキャンセルします。
タイルEに敵がいるとしたら、タイルBには入れるけど出れないということです。
緑色で囲ったところのルートをキャンセルすれば、実装できますね。
とはいえまずはタイルBを探す(=敵に隣接しているマスを探す)ことからスタートです。
実践① 敵の周囲のタイルをリストアップ
Unitがどこにいるのかの情報は、実はGridManagerに保存されています。
「Grid Units」がそれです。
Index番号とそのIndex番号に居るユニットが表のように保存されています。
敵と味方の判別はそれぞれのユニットに保存されている「Faction」という情報から調べられます。
ではこれで、いま経路検索しているキャラクターの敵がどこにいるのか調べましょう。先日上げた、経路検索に関するメモの通り、経路検索はGrid Managerの「Run Pathfinding」関数から実行されているので早速見てみましょう。
Grid Manager 「Run Pathfinding」関数 |
なんだかよくわかりませんが、もう一個「Run Pathfinding」があります。これはこの関数とは別物で、"BP Path"の「Run Pathfinding」関数です。ややこしい。
次に、全キャラクターの情報を引っ張り出しましょう。
先ほどのGrid Unitsから「Values」を取り出して、「For each Loop」をつなぎました。
これはGrid Unitsに保存されているすべてのユニットを、一つ一つ調べていく作業ですね。
あとは経路検索しているキャラクターと照合すればいいですね。
Get Factionと、先ほどのFactionのデータを照合します。「==」で検索すると出てくるので、それをBranchにつなぎます。
True = 経路検索しているキャラに対して味方のキャラクター の場合
False =経路検索しているキャラに対して敵のキャラクター の場合
となります。
Falseの時、Index番号の情報が欲しいですね。
Index番号は、Grid Unitsの中に格納されていたので、ValuesだけでなくKeysノードを使ってIndex番号を取り出しておきます。
配列から~~番目の情報を取り出す際に使うノードは、そう、Get(Copy)ノードですね。
Keysノードをつないだのち、Copyで検索したらでてきます。
我々が調べたかったのは、敵キャラのいるタイルではなく、その周囲のタイルを知りたいのです。これでは終わりません。
しかーーーし!!
このアセットはすごいことに、特定のタイルの、周囲にあるタイルの情報まで格納してくれているのです。それがGrid Edgesです。早速使ってみましょう。
Grid EdgesをGetして、Findノードを作成します。そこに先ほどGet(COPY)で出てきたIndex番号をつないでやります。すると構造体が出てきます。
この構造体は、特定のタイルの周囲にあるタイルのIndex番号がまとまって格納されているようなものです。6つ保存されています。
※端っこのタイルは3つとかしか隣り合ってませんが、その場合6つのうち3つはマイナスの値が保存されています。
構造体のままだと扱いが難しいので、分割してやりましょう。青いところのピンで右クリックして分割を選択します。
Keysで、敵の周囲のマスのIndex番号をまとめて取り出せます。先ほどのBranchのFalseにつなぎましょう。
さて、あとはこのIndex番号を格納する変数が必要そうですね。
Integerの配列変数を定義します。名前は適当にZocAreaとでもしておきましょう。この配列変数をGetして、先ほどのKeysから出てきたIndex番号の羅列をAppendして追加しておきます。
さて、これだけだと次のキャラクターに移った際にどんどん敵周囲キャラが増えていってしまうので、この変数は最初に初期化する必要がありますね。
Clearノードを一番最初に挟みましょう。
最後に、For each loopのCompletedのピンに、最初からあった「Find or Createなんちゃら」のノードをつなげます。
これで、敵周囲マスをすべてリストアップできました。疲れました。コンパイルしておきましょう。
実践② 敵の周囲のマスからスタートする経路検索を全てキャンセル
こっからが本当の地獄だ…
のはずなんですが、作業自体は一瞬で終わります。ここにたどり着くには数々の地獄がありましたが。
先ほどの、BP Pathの方のRun Pathfiding関数をダブルクリックして開きますと、自動的にBP Pathのタブが開くと思います。左上の関数一覧から「AddTileIfValid」をダブルクリックして開きます。右の方にあるここを書き換えます。
こんな感じで、変数のタブからGridManagerRefをGetし、そこから引っ張ってZocArea変数を取り出します。
Containsノードを作成。左側の緑のピンを引っ張って、Edgeで検索してEdge Indexを出しましょう。
あとは右側の赤いピンを引っ張ってBranchノードを作れば終わりです。
ブランチノードは、上のブランチのFalseにつなぎます。
Trueはリターンノードに、FalseはもともとあったADDノードにつながるようにします。
これで終了。コンパイルして実行しましょう。
無事できました。お疲れさまでした!!
またいいネタがあったら載せておきます。
0 件のコメント:
コメントを投稿