Slide 1

Slide 1 text

Unity 1week でつくったゲームに AIを実装してみる 2021/1/13 とりすーぷ

Slide 2

Slide 2 text

自己紹介 •とりすーぷ • @toRisouP • VR系の開発してる • Microsoft MVP • (Developer Technologies) • 最近はVRChatしてます illustrations by kota(@kt_kkz)さん

Slide 3

Slide 3 text

あけろ!爆裂駐車場! https://unityroom.com/games/bakuretsu

Slide 4

Slide 4 text

Unity 1Weekでつくったゲーム •「あけろ!爆裂駐車場!」 • 次から次へと車が押し寄せる駐車場を空けるゲーム

Slide 5

Slide 5 text

駐車場をあけろ! •次々にやってくる車を駐車場から排除しろ! • 車はぶっ壊してもOK • 外野にふっとばしてもOK!

Slide 6

Slide 6 text

武器 •武器アイテムを拾って装備を変えよう!

Slide 7

Slide 7 text

ワンミスで終了だ! •やられたら終了! • 爆発に巻き込まれたり、フィールドから落ちたら終わり

Slide 8

Slide 8 text

こだわりポイント •VRM読み込みに対応 • 好きなアバターであそべる!

Slide 9

Slide 9 text

AIを実装してみる

Slide 10

Slide 10 text

このゲームにAIを実装してみよう •目標 • AIが自動的にプレイヤを操作する • 状況を判断して適切な行動を取り続ける

Slide 11

Slide 11 text

できたもの 公開してます:https://unityroom.com/games/akebaku_ai

Slide 12

Slide 12 text

手法 •「Behavior Tree」 • 階層構造をもったステートマシンみたいなもの • “状態”ではなく“行動”について評価をする

Slide 13

Slide 13 text

Behavior Tree Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する • 階層構造をもったグラフで AIの行動パターンを定義する • 深さ優先、左端優先

Slide 14

Slide 14 text

Node Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する ノード → ノード → ノード → ノード → • 各要素を「ノード」と呼ぶ

Slide 15

Slide 15 text

• 各ノードは状態を持つ • 成功 • 失敗 • (実行中) • (待機中) 状態 Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する

Slide 16

Slide 16 text

ノードの種類 Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する • ノードは大きく2種類に分類される • Control Flow ノード • Task ノード

Slide 17

Slide 17 text

Behavior Tree Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する • Task • 実際にAIがとる「行動」を表す これらがTask→

Slide 18

Slide 18 text

Control Flow Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する • Control Flow • 全体の”流れ”を司るノード • 種類がいくつかある • SelectorとSequence の2つがメインに使われる Control Flow

Slide 19

Slide 19 text

Sequence Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する • Sequence • 子のノードを左から実行 • 子がすべて成功したら「成功」 • 子が1つでも失敗したら「失敗」 になり即終了 • 要するにAND

Slide 20

Slide 20 text

Selector Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する • Selector • 子のノードを左から実行 • 子が成功した時点で「成功」 • 子がすべて失敗したら「失敗」 • 要するにOR

Slide 21

Slide 21 text

Repeat Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する • Repeat • 子の処理をずっと繰り返す

Slide 22

Slide 22 text

このグラフの挙動 Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する

Slide 23

Slide 23 text

このグラフの挙動 Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する 1. 敵が近くにいるか判定する 2. 見つけた敵に向かって移動する 3. 攻撃する

Slide 24

Slide 24 text

このグラフの挙動 Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する 1. 敵が近くにいるか判定する 2. 見つけた敵に向かって移動する 3. 攻撃する ↑が失敗したら↓を実行する 1. フィールド中央に移動する 2. 待機する

Slide 25

Slide 25 text

Case1: 敵が近くにいた場合 Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する 実行中 待機中 実行中

Slide 26

Slide 26 text

Case1: 敵が近くにいた場合 Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する 「1.敵が近くにいるか?」を実行する 実行中 待機中 待機中 実行中 実行中 待機中

Slide 27

Slide 27 text

Case1: 敵が近くにいた場合 Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する 「1.敵が近くにいるか?」が成功する → 次に処理が移る 実行中 待機中 待機中 実行中 実行中

Slide 28

Slide 28 text

Case1: 敵が近くにいた場合 Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する 「2.対象に移動する」が成功する → 次に処理が移る 実行中 待機中 実行中 実行中

Slide 29

Slide 29 text

Case1: 敵が近くにいた場合 Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する 「3.攻撃する」が成功する → Sequence自体が「成功」する 待機中 実行中

Slide 30

Slide 30 text

Case1: 敵が近くにいた場合 Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する Selectorも「成功する」 → Repeatによって最初に戻る 待機中

Slide 31

Slide 31 text

Case2: 敵が近くにいなかった Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する 実行中 待機中 実行中 「1.敵が近くにいるか?」を実行する 待機中 実行中 待機中

Slide 32

Slide 32 text

Case2: 敵が近くにいなかった Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する 実行中 待機中 実行中 「1.敵が近くにいるか?」を実行する → 失敗する 待機中 待機中

Slide 33

Slide 33 text

Case2: 敵が近くにいなかった Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する 待機中 実行中 「1.敵が近くにいるか?」を実行する → 失敗する → 続きは実行されずSequence自体が 失敗となる 待機中 待機中

Slide 34

Slide 34 text

Case2: 敵が近くにいなかった Selector Sequence 敵が近くに いるか? Sequence 対象に 移動する 攻撃する フィールド 中央に 移動する 待機する 実行中 実行中 Selectorにより次のブロックに処理が移る 待機中 待機中 実行中

Slide 35

Slide 35 text

アセットを使うと楽 •Behavior Designer

Slide 36

Slide 36 text

昔に解説してるので参考になれば •【Unity】 Behavior TreeでAIを作る • https://www.slideshare.net/torisoup/unity-behavior-treeai

Slide 37

Slide 37 text

UnityでAIをつくっていく

Slide 38

Slide 38 text

AIを実装していく •今回は「Behavior Designer」を使って、 「あけろ!爆裂駐車場!」にAIを実装する

Slide 39

Slide 39 text

方針 「Playerのコンポーネントには手を加えない」 • プレイヤーの移動、攻撃、アイテム装備…、 すべて同じコンポーネントをそのまま使いまわす

Slide 40

Slide 40 text

差し替える部分はどこか? •「入力イベント」のみを差し替える

Slide 41

Slide 41 text

人間が遊ぶ場合 人間 入力イベント プレイヤキャラクタ

Slide 42

Slide 42 text

AIの場合 AI 入力イベント プレイヤキャラクタ

Slide 43

Slide 43 text

イベントを使い回せば ここの処理は全く同じにできる! 入力イベントの発行者が 異なるだけで

Slide 44

Slide 44 text

入力の抽象化レイヤを挟む 人間やAIから入力されたイベント をいい感じに変換するレイヤ

Slide 45

Slide 45 text

抽象化 •入力イベント発行機構を抽象化しておく

Slide 46

Slide 46 text

人間が操作するとき •キーボードからの入力をイベントに変換する

Slide 47

Slide 47 text

AIが操作するとき •AIからの入力をイベントに変換する • AIに「ゲームパッド」を持たせるイメージ

Slide 48

Slide 48 text

プレイヤ初期化時に差し替える •差し替えるのは基本これだけ • これでAIがプレイヤを操作する口ができた

Slide 49

Slide 49 text

これで入力イベントが共通化できた IInputEventProvider

Slide 50

Slide 50 text

他の下準備 •AI用にいくつか下準備をしておく

Slide 51

Slide 51 text

Playerにコンポーネントを追加 •「Behavior Designer」と「NavMeshAgent」

Slide 52

Slide 52 text

AI用の情報提供クラス •AIがゲームの状態を知るための仲介クラス

Slide 53

Slide 53 text

AIの実装

Slide 54

Slide 54 text

ロジック 1. 燃えている車があったら、逃げる 2. アイテムが近くにあったら、拾う 3. 車があったら、近づいて攻撃する 4. ステージ中央に向かう

Slide 55

Slide 55 text

ロジック 1. 燃えている車があったら、逃げる 2. アイテムが近くにあったら、拾う 3. 車があったら、近づいて攻撃する 4. ステージ中央に向かう 優先度が高い処理から試行していく 条件を満たさない場合は次の処理を試す

Slide 56

Slide 56 text

AIの実装手順 1. Taskノードを自作する 2. ノードグラフを構築する 3. 動作確認して調整する

Slide 57

Slide 57 text

Taskノードの自作 •Behavior Designerは“行動”を自作可能 • C#のスクリプトを書けば簡単に作れる • 必要なタスクを自分で作ってしまえばOK

Slide 58

Slide 58 text

車を探すTask

Slide 59

Slide 59 text

車を探すTask 毎フレーム実行される Runningを返すと継続 Success / Failureを返すと終了

Slide 60

Slide 60 text

例:「移動する」Taskの実装 InputEventProviderに渡してPlayerの移動に反映する NavMeshAgentから得た経路の情報を

Slide 61

Slide 61 text

結果

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

4つのブロックにわかれている

Slide 64

Slide 64 text

1.燃えている車が近くにあったら逃げる 燃えている車が 近くにあるか? 逃げる先を決定 移動する ちょっと待機

Slide 65

Slide 65 text

2.アイテムが近くにあったら拾う アイテムが 近くにあるか? 移動する

Slide 66

Slide 66 text

3.車があったら攻撃しにいく 車を探す 移動する 攻撃する

Slide 67

Slide 67 text

4.ステージ中央に向かう

Slide 68

Slide 68 text

動作させながら微調整 •パラメータを調整する •挙動を細かく調整する

Slide 69

Slide 69 text

例:調整箇所 •「攻撃するために移動する」 • 武器ごとにリーチに差がある • ロケランは自爆の可能性がある → 装備武器ごとに対象にどれだけ近づくか変える

Slide 70

Slide 70 text

調整 •武器ごとにどれだけ車に近づくか変える • 近接武器はより近づく、ロケランは遠くで止まる

Slide 71

Slide 71 text

他にも調整したい箇所は山程ある •移動中に障害物でスタックしたときどうする •攻撃が届かないシチュエーションを減らす •場外に飛んでいった車に反応しないようにする •戦車から逃げる •爆風に自分から突っ込まないようにする

Slide 72

Slide 72 text

まとめ •Behavior Treeはいいぞ • 比較的かんたんにAIを実装できる! • それっぽく動くところまでの実装時間は約3時間 • ただしそこから微調整で+6時間くらい… • あらかじめAIの実装を許容する設計にしておく必要はあり • 適切に処理を抽象化しておこう

Slide 73

Slide 73 text

以下おまけ

Slide 74

Slide 74 text

AIを移動させる •Unityには「NavMesh」という機能がある • 経路探索を用いてオブジェクトを移動させる機能 • これを使えば障害物を避けていい感じに移動してくれる

Slide 75

Slide 75 text

NavMeshAgentの利用方法 •NavMeshAgent • NavMeshに沿ってオブジェクトを動かすコンポーネント • デフォルトだとこのコンポーネントで勝手に移動していく → NavMeshAgentによる自動移動を今回はOFFにする

Slide 76

Slide 76 text

NavMeshを使ったAI操作

Slide 77

Slide 77 text

NavMeshを使ったAI操作 NavMeshAgentの認識する「現在地」を 自分のtransform位置に補正する

Slide 78

Slide 78 text

NavMeshを使ったAI操作 NavMeshAgent.desiredVelocity で 経路探索の結果から移動するべき方向が取得できる

Slide 79

Slide 79 text

NavMeshを使ったAI操作 移動するべき方向をInputEventProviderに渡して Playerの移動入力とする