
ビジュアルスクリプティングのステートマシン
Tutorial
Beginner
+10XP
25m
(9)
Unity Technologies

ゲームオブジェクトにスタンディング、ランニング、ファイティング、ダメージを与える等、アクションが切り替わる状態を与えることができ、それぞれの状態は異なる状況下で異なるイベントに対応します。本チュートリアルでは、クライブの地下室にあるトラップドアのステートマシン (State Machine) を作ります。
このチュートリアルを終える頃には、以下のことができるようになっています:
- ステートグラフ (State Graph) とスクリプトグラフ (Script Graph) の違い
- 新しいステートグラフを作成する
- ステートマシンのステート (State) を表すスクリプトグラフを作成する
- ステートマシンの様々なスクリプト間を移動する
- 望ましい結果が得られた場合は、ステートグラフで遷移を考案し、構成する
- 既存の複雑なビジュアルスクリプトを解釈する
- 既存のスクリプトグラフをステートマシンで使用できるようにする
Languages available:
1. 概要
ビジュアルスクリプティングでは、State (ステート) とはゲームオブジェクトに定義する動作や状態のことです。アニメーターコントローラーでアニメーションのステートマシンを使用したことがある方は、ビジュアルスクリプティングのステートマシンのコンセプトをご存知でしょう。また、ステートを切り替えることができる「人格」と考えることもできます。各ステートでは、ゲームオブジェクトは異なる行動をとり、異なる刺激(イベントなど)に反応します。
たとえば、ドアであれば、開いている (Open)、ロックされている (Locked)、またはロック解除されている (Unlocked) 、ゲームのプレイヤーキャラクターであれば、立っている (Standing)、走っている (Running)、戦っている (Fighting)、またはダメージを受けている (Hurt) といった具合です。いずれの場合も、ボタンやキーを押したときにゲームオブジェクトが何か違う動作をするようにしたいし、特定の条件下でのみ状態が変化するようにしたいのです。このような場合、イベントや変数、たくさんの If ユニットを使ってプログラミングすることもできますが、ステートマシンの方がはるかに簡単で効率的です。
本チュートリアルでは、3 つのステート (Open, Closed, Filled) を持つトラップドアのステートマシンを作成します。クライブはレバーを使って Open と Closed の間の状態を変えて、木箱をドアの下の穴に押し込むことで Filled にすることができます。
各ステートでは、トラップドアは異なるイベントに反応します。例えば、トラップドアのステートが Open の場合、クライブはセルに入ることができませんが、木箱は入ることができ、穴に落ちることができます。ただし、トラップドアが Closed または Filled の場合は、クライブも他の動くオブジェクトも普通に通過することができます。また、クライブは Open の状態でしか穴を埋めることができません。ステートマシンは、このような異なる動作を管理するのに最適です。
ステートマシンの仕組み
ここでは、ステートマシンの構造を理解するために、クイックツアーを用意しました:
1. Hierarchy で、ObjectRoot > Trap Door と選択します。
2. Inspector には、Script Machine と State Machine が表示されます。Script Machine は、TrapDoorLever をテストするために用意したものです。Script Machine のチェックボックスを外して無効にし、Script Machine を有効にします。
3. Edit Graph ボタンを選択します。これで State Graph が表示され、ウィンドウの名前が State Graph に変わりました。State Graph には、ユニットの代わりに、ステートと遷移が含まれています。
このステートグラフには、Open という名前のスクリプトステートが 1 つ含まれています。このユニットのテキストは、その中のイベントを示しています。
4. Open のステートをダブルクリックすると、その Script Graph が開きます。これは、今まで作ってきたグラフのようなものです。トラップドアをアニメーションさせるフローを開始する On Enter State ユニットと、Cancel Movement につながる Want To Enter Cell ユニットが表示されます。このスクリプトは後ほど完成させます。
5. Script Graph ウィンドウのメニューバーの左上、Zoom の左にある TrapDoor を選択すると、State Graph に戻ります。
6. State Graph の Graph ウィンドウで右クリックし、Create Script State を選択します。新しい Script State が表示されます。
1. Graph Inspector で、Script State の Title を Closed に変更します。
2. Closed ステートをダブルクリックすると、その Script Graph が表示されます。デフォルトでは、3 つのイベントが含まれています。Update と On Exit State を削除します。On Enter State は後で編集することになります。
3. Script Graph ウィンドウのメニューバーの左上にある TrapDoor を選択すると、再び State Graph に戻ります。
4. Closed ステートを右クリックし、Duplicate を選択します。
5. Graph Inspector で、この Script State の Title を Filled に変更します。
ステートグラフは、下の画像のようになります。
各ステートのスクリプトグラフを編集して、各ステートの動作を定義します。
2. Open ステートのスクリプティング
今回の State Graph では、Open ステートが最も複雑です。まずは、おなじみの 2 つの既存フローを編集してみましょう。
On Enter State
On Enter State では、このステートが入力されるたびに 1 回実行されます。ステートが Open に変わると何が起こるのか?もちろん、トラップドアが開きます。
AnimAndFX スーパーユニットを使って実現します。TrapDoor ゲームオブジェクトのアニメーションでは、パラメーター名を Switched とし、True を入力するとオープニングアニメーションが再生されます。
Want To Enter Cell
ここでの既存のフローは、鍵のかかったドアを開ける方法がないだけです。その代わり、このフローでは、トラップドアが開いているときにクライブが入ることはできませんが、木箱など穴に落ちる可能性のあるその他のオブジェクトは許可する必要があります。
落下可能なオブジェクトとそうでないオブジェクトを区別するにはどうしたらよいでしょうか?落下可能なオブジェクトにのみ作成される CanFall という名前の Object 変数で区別することができます。次に、この変数の存在を Has Variable ユニットで確認し、Boolean を出力します(すべてのゲームオブジェクトに Boolean CanFall 変数を追加するよりもずっと簡単です!)。
次に、この変数の存在を Has Variable ユニットで確認し、Boolean を出力します(すべてのゲームオブジェクトに Boolean CanFall 変数を追加するよりもずっと簡単です!)。
CanFall が存在する場合、どうしますか?穴に落ちるオブジェクトのスクリプトはどうするのでしょうか?この効果は、ゲームの最初のレベルで見られましたね。既に行われた作業を繰り返すのではなく、ゲームの最初のレベルからビジュアルスクリプトを借りてみましょう。
3. Sample シーンへの旅に出よう
ゲームの最初のレベルでは、クライブは鍵を手に入れるために木箱を穴に押し込まなければなりません。そのビジュアルスクリプトを探して借りてみましょう。
1. 現在のシーンを離れる前に、作業内容を保存します。
2. Project ウィンドウで、Assets > VisualScriptingTutorial > CompleteGame > Scenes > SampleScene の順に移動し、シーンを開きます。
3. Scene ビューで、クライブが鍵を手に入れるために埋めた穴を選択します。Hierarchy では、Pit という名前のオブジェクトの子であることがわかります。
4. Inspector ウィンドウで、Script Machine コンポーネントを見つけ、Edit Graph ボタンを選択します。
このスクリプトを解釈する
これは複雑なビジュアルスクリプトですが、あなたはそれを理解するのに十分な専門知識を持っています。以下の要約を読む前に、ビジュアルスクリプトを読んでみてください。
要約:
- Want To Enter Cell のフローでは、CanFall が見つかった場合、FallingMovingObject という名前の Graph 変数に、セルに入りたい Moving Object のコピーが設定されます。
- Update フローでは、各フレームで以下の処理を行います:
- FallingMovingObject が Null であるかどうかを調べます。
- Null の場合、このフレームでは何も起こりません。
- それが Not Null であれば、落下するゲームオブジェクトのコピーを取得します。
- FallingMovingObject を穴の底に向かってごく短い距離だけ移動させます。
- 穴の底からオブジェクトまでの距離が実質的に 0 (0.001 以下) であれば、FallingMovingObject を Null に戻し、次のフレームでこのフローがオブジェクトを動かし続けるのを止めます。
これらの情報をもとに、このロジックに沿って、Open ステートでこの Update フローを機能させることができるかどうかを確認します。必要に応じて、次のステップの画像を使って作業を確認します。作業内容は必ず保存 (Ctrl + S / Cmd + S) してください。
まだテストすることはできません。まずステートマシンの構築を終えなければなりません。自分が作ったものを確認したい場合は、Samples サブフォルダーにある Final_TrapDoorOpenState をご覧ください。
成功したら、次のステップに完了マークをつけます。行き詰まった場合は、次のステップの手順に従ってください。
4. Sample シーンの Update フローを適用する(手順)
この Update フローを利用するための準備:
1. SampleScene のビジュアルスクリプトにある Update フローのユニットをすべてコピーし、Open ステートの Script Graph の空白部分にペーストします。
2. Blackboard の TrapDoor に FallingMovingObject という Graph 変数を作成します。その Type は Null のままにしておきます。
3. Want To Enter Cell フローでは、CanFall が存在する場合、変数 FallingMovingObject をイベントによって出力される Moving Object に設定します。これにより、Update フローが動作するようになります。
この Update フローを Open ステートに合わせてみましょう:
4. Update フローの最後のユニットは、Passable という変数を True に設定するユニットですが、トラップドアが通れるかどうかは State Machine が処理するので、追跡する必要はありません。このユニットを削除します。
5. Update フローの最後に、Custom Event Trigger ユニットを追加します。このイベントを HoleFilled と名付けます。(このイベントのスクリプトは後で書きます)。
6. グループやコメントを使って、このフローをどのように改善しますか?このスクリプトで何が起こっているかを把握するために、必要に応じてそれらを追加してください。
7. 作業内容を保存します。
このスクリプトは大きすぎて画像に収まりませんが、自分の作業を確認したい場合は、Samples サブフォルダーの Final_TrapDoorOpenState をご覧ください。
これで Open ステートの準備が整いました。ステートに入るとアニメーションが再生され、CanFall 変数で落下を指定されたオブジェクトだけがエントリーできるようになり、Update イベントを使って落下するオブジェクトをフレームごとに穴に移動させます。このテストは後で行います。
5. Closed ステートのスクリプティング
TrapDoor が Closed ステートでは、他のセルと同じように、どんなオブジェクトでも通過することができます。Closed ステートで必要な動作は、そのステートになったときにアニメーションを再生することだけです。
Open ステートで行ったのと同じ方法で、AnimAndFX スーパーユニットを Closed ステートの Script Graph に追加し、Animation Name を Switched、Animation Value をここではFalse とします。
6. Filled ステートのスクリプティング
Filled ステートにはアニメーションはありません ー Open ステートの Update フローによって、オブジェクトはすでに穴に入れられています:
1. Type が GameObject、Value が FourthDoor の Object 変数を作成します。
2. 下の画像と一致するように、Filled ステートの Script Graph をスクリプティングします:
7. 遷移のスクリプティング
3 つのステートのスクリプトグラフはできていますが、このステートマシンのロジックの一部がまだ欠けています。TrapDoor ゲームオブジェクトは、どのような条件でステートが変化するのでしょうか?次に、ステートグラフに遷移 (transitions) を追加して、その条件を設定します。
遷移により、任意のステートが他のステートへ切り替かわることができる条件を指定することができます。ここでは、このステートマシンの遷移のルールを紹介します:
穴が埋まってしまうと、ステートを変更する方法がないことに注意してください。限られた数の遷移しかできません。ステートマシンは、ゲームのプレイ中にどのような状況が起こりうるかを指定するための論理的な構造を提供します。
上記の表に従って、このステートマシンに遷移を追加しましょう:
1. TrapDoor オブジェクトの State Graph を開きます。
2. Open から State に移行するには、Open ステートを右クリックして Make Transition を選択し、Closed ステートをクリックします。State Graph は以下の画像のようになります。
3. 2 つのステートの間の遷移をダブルクリックすると、この遷移の Script Graph が表示されます。State: Trigger Transition というユニットがデフォルトでスクリプトに含まれています ー 遷移を発生させるためには、このユニットでスクリプトを終了させる必要があります。
4. この遷移を引き起こすイベントの名前は LeverSwitched です(覚えていますか? このイベントは Lever の ON / OFF 時に発生します)。そのイベントは Boolean 値を渡します。Custom Event ユニットを追加し、LeverSwitched という名前を入力し、Arguments の値を 1 に設定します(これは TrapDoorLeverScript の Custom Event Trigger ユニットと一致します)。
5. If ユニットを追加し、Custom Event ユニットからフローと値の両方のコネクターを接続します。
6. If ユニットの True ブランチを State: Trigger Transition ユニットに接続します。
7. State Graph に戻ります。
8. Closed から Open へのトランジションを作成するには、Closed ステートから Open ステートへの遷移を作成し、LeverSwitched イベントの値が True の場合に遷移が発生するようにする以外は、上記の手順を繰り返します。
9. State Graph に戻ります。
10. Open から Filled への遷移を作成します。そのスクリプトでは、HoleFilled イベントが発生したときに、遷移を行うように指定するだけです。
11. 作業内容を保存します!
最終的には下図のようなステートグラフが完成します。
8. ステートマシンのテスト
テストの準備ができました。クライブがどうやって木箱を落としトラップドアの穴に入れるのかがわかったら、ステートマシンのすべてのステートと遷移を見ることができます。さらに、クライブのタスクが完了したときのご褒美も用意されています。
エラーが発生したり、ステップを間違えたりした場合に備えて、完成したステートマシンは Samples サブフォルダーの Final_TrapDoorStates ファイルにあります。
注意:Final_TrapDoorStates で使用されているスーパーユニットは、サンプルコピーであり、あなたが作成したスーパーユニットのビジュアルスクリプトではありません。