前の記事でVisual Scriptingの使い方についてみていきました。
今回はより実践的にキャラクター移動スクリプトをVisual Scriptingを使って作成していきたいと思います。
C#スクリプトを使ったキャラクター移動スクリプトは何度も作成しているので、C#スクリプトで作ったものをVisual Scriptingで同じように作ってみるという感じです。
キャラクターのAnimatorコンポーネントに取り付けるAnimatorControllerは既に作成してあり、Float型のSpeedというアニメーションパラメータの作成をし、Idle状態、Walk状態を作り、
Idle→WalkはSpeedが0.1以上
Walk→IdleはSpeedが0.1以下
の時に遷移するようにしておきます。
ここら辺は以下の記事を参照してください。
Visual Scriptingでキャラクター移動スクリプトを作成する
Visual Scriptingでキャラクター移動スクリプトを作成する前に、C#スクリプトで動くキャラクターを作って比較する事にします。
C#スクリプトキャラクターとVisual Scriptingキャラクターの作成
キャラクターモデルにはスタンダードアセットのEthanを使用し、Animatorコンポーネントには作成したAnimatorControllerを設定し、CharacterControllerコンポーネントを取り付けてコライダのサイズを調整しておきます。
また新しくCharacterScriptという名前のC#スクリプトを作成し、これも取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class CharacterScript : MonoBehaviour { private CharacterController characterController; private Animator animator; private Vector3 velocity; [SerializeField] private float moveSpeed = 2f; // Start is called before the first frame update void Start() { characterController = GetComponent<CharacterController>(); animator = GetComponent<Animator>(); } // Update is called once per frame void Update() { if(characterController.isGrounded) { velocity = Vector3.zero; var input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); if(input.magnitude > 0f) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input.normalized); velocity = transform.forward * moveSpeed; } else { animator.SetFloat("Speed", 0f); } } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * Time.deltaTime); } } |
Ethanのインスペクタは以下のようになります。
シーンには地面となるPlane等のゲームオブジェクトを設置し、キャラクターを移動出来る事を確認してください。
問題なく動くようであればEthanゲームオブジェクトを選択し、Ctrl+Dキーを押して複製し、インスペクタのCharacterScriptコンポーネントを削除します。
さらに、Add ComponentからScript Machineコンポーネントを取り付けます。
Assetsフォルダで右クリックからVisual Scripting→Script Graphを選択し、名前をCharacterScriptとし、Script MachineコンポーネントのGraphにドラッグ&ドロップして設定します。
CharacterScriptグラフの中身を作成する
キャラクターの設定が出来たので、C#スクリプトで行った処理をVisual Scriptingに置き換えて作成していきます。
C#スクリプトではcharacterController、animator、velocity、moveSpeedフィールドを使用しています。
characterController、animator、velocityはグラフ変数、moveSpeedはオブジェクト変数として定義することにします。
ヒエラルキーのEthan(1)ゲームオブジェクトを選択し、CharacterScriptスクリプトグラフウインドウで変数を設定します。
characterControllerとanimatorは自身のゲームオブジェクトから取得するのでスクリプトグラフでStartユニットを作成し、そこからポートを繋いでグラフ変数にそれぞれ設定します。
次にUpdateユニットを作成し、そこからキャラクターの移動処理やアニメーターの操作処理を追加していきます。
Updateユニットからifユニットにフローを繋ぎ、グラフ変数のcharacterControllerを取得してプロパティのisGroundedをチェックしてそれがTrueだった時は次のユニットに移動します。
Falseだった時は重力の計算をし、移動処理をする部分に行きますが、とりあえず置いておきます。
Trueだった時は以下のようなユニットを作成します。
Set Variableでグラフ変数のvelocityにGet Zeroユニットを接続し、接地した時は移動速度のXYZを全て0にします。
その後Input ManagerのHorizontalとVerticalの値を取得し、新しいVector3の値を作ります。
新しく作ったVector3の値のMagnitude(ベクトルの長さ)を取得し、それが0より大きい(移動の入力がされている)時はアニメーターのSpeedアニメーションパラメーターに入力値のMagnitudeを設定します。
入力値が0以下の場合はアニメーションパラメーターのSpeedに0を設定します。
次はキャラクターの移動値が0より大きかった時の処理です。
キャラクターの現在の位置に入力した方向(単位ベクトル)を足した向きにLookAtを使ってキャラクターを向けています。
キャラクターの向きを変更したら、キャラクターの向いている向きに移動スピードを掛けて速度を計算します。
Get Forwardユニットでキャラクターの向きを取得し、それにオブジェクト変数のmoveSpeedをかけ、それをvelocityに代入しています。
次はC#スクリプトで接地かどうかを判断しているif文の後の重力とCharacterControllerのMoveメソッドを使った実際の移動処理の部分を作成します。
まずは重力の計算処理を作ります。
Get Gravityユニットで重力値を取得したものとGet Delta Timeをかけて1フレームの重力での移動値を計算し、グラフ変数のvelocityと足します。
それを再度velocityに入れています。
重力の計算をしてvelocityに値を設定するSet VariableユニットにはcharacterControllerのisGroundedがTrueであれ、Falseであれ必ず計算する必要があります。
なので「接地しているかどうか」グループにあるifユニットと「移動速度計算」グループのSet Variableユニット(velocityの値を設定しているやつ)の両方のフローポートからフローを繋ぐ必要があります。
最後にCharacterControllerのMoveメソッドで実際の移動処理をする部分を作成します。
ここではvelocityにGet Delta Timeユニットをかけた値をCharacterControllerのMoveユニットに接続しています。
全体のユニットと接続は以下のようになります。
これで機能が出来ました。
Unityを実行してC#スクリプトのキャラクターと動きが変わらないか確認してください。
ステートグラフを使ってドアを開く機能を作成する
Visual Scriptingを使ってキャラクター移動の機能が出来ました。
次はドアを作成して、キャラクターが近づいたらドアを開く、離れたらドアを閉じるというような機能を作成してみます。
ドアの状態に合わせて処理を変更したいので、ステートグラフを使います。
ドアのゲームオブジェクトの作成
ドアのゲームオブジェクトはCubeを使って作成しますが、ドアの開閉のアニメーションはドアの親の位置を基点に回転させ作ります。
まずはヒエラルキーにCubeを作成し、名前をDoorObjとし、インスペクタのTransformで以下のように設定します。
次にヒエラルキーに空のゲームオブジェクト(Create Empty)を作成し、名前をDoorBaseとし、DoorObjの回転の基点となる位置に移動させます。
インスペクタで見ると以下のような感じです。
さらに空のゲームオブジェクトを作成し、名前をDoorとします。
次にDoorObjをDoorBaseの子要素に移動させます。
次にDoorBaseをDoorの子要素に移動させます。
ヒエラルキーは以下のようになりました。
さらにDoorゲームオブジェクトのインスペクタのTransformを変更します。
Doorにはキャラクターが侵入したかどうかを判定するコライダを取り付けます。
範囲として使用するのでSphere ColliderのIs Triggerにチェックを入れます。
床ゲームオブジェクトの作成
さらにヒエラルキーにPlaneを作成し、インスペクタで以下のように設定します。
床とドアは以下のようになりました。
ドアの開閉アニメーションの作成
ドアの開閉アニメーションを作成します。
UnityメニューのWindowからAnimation→Animationを選択し、アニメーションウインドウを開きます。
ヒエラルキーのDoorBaseゲームオブジェクトを選択し、AnimationウインドウのCreateを押して、新しく出てきたウインドウでCloseという名前で保存します。
そのままAnimationウインドウのCloseの部分を押し、Create New Clipを選択し、Openという名前で保存します。
Animationウインドウの録画ボタンを押して、ヒエラルキーのDoorBaseを選択し、シーンビューでY軸を回転させます。
0:00の部分ではDoorBaseのTransformのRotationのYを0、1:00の部分ではDoorBaseのTransformのRotationのYを-130となるようにキーフレームを打ちます。
AssetsフォルダにあるOpenアニメーションクリップを選択し、インスペクタのLoop Timeのチェックを外してループ再生しないようにします。
Animationウインドウでのアニメーションの作成については、以下の記事を参照して作成してみてください。
DoorBaseアニメーターコントローラーでBool型のOpenedというアニメーションパラメーターを作成し、
Close状態とOpen状態を作ります。
それぞれ作成したアニメーションクリップを設定します。
また、
Close→OpenへはOpenedがtrueの時
Open→CloseへはOpenedがfalseの時
に遷移するようにします。
これでドアのアニメーションが出来ました。
ドア用のステートグラフを作成する
ドア用のステートグラフを作成し、ドアの状態によって処理を分けるようにします。
Assetsフォルダ内で右クリックからVisual Scripting→State Graphを選択し、名前をDoorScriptとします。
DoorScriptステートグラフを開いて、右クリックからCreate Script Stateを選択します。
最初に作成したユニットがデフォルトの状態になります。
他の状態ユニットをスタートにしたい時はユニットを選択し、右クリックからToggle Startを選択して切り替えます(スタート状態はユニットの上部が緑色になります)。
Startユニットを選択し、Graph Inspectorで名前をCloseに変更します。
名前を変更したらCloseユニットをダブルクリックし、中身を記述していきます。
元からある他のイベントユニットは使わないので削除しておきます。
On Trigger Enterユニットで他のコライダが侵入した時にCompare Tagを使ってPlayerタグが設定されたゲームオブジェクトかどうかを判断します。
Trueの時はAnimatorのSet Boolユニットを使って自身のゲームオブジェクトの子(DoorBase)のAnimatorのOpenedをTrueにします。
その後、Custom Event Triggerユニットに接続しDoorOpenというカスタムイベントを発生させます。
ここまで出来たら親のDoorScriptの編集に戻ります。
Close状態を選択し、右クリックからMake Transitionを選択した後にマウスの左ボタンを押して確定し、新しい状態への遷移を作成します。
遷移条件部分をダブルクリックし、遷移条件を設定します。
Close状態の中でDoorOpenというイベントを発生させたので、このイベントが発生したら遷移するようにします。
DoorScriptステートグラフに戻ります。
先ほど作成した新しい状態を選択し、Graph Inspectorで名前をOpenに変更します。
Close状態のユニットをCtrl+Aキーで全て選択した後、Ctrl+Cキーを押してコピーし、Open状態の中に貼り付けます。
元からある他のイベントは使わないので削除します。
最初のOn Trigger EnterだったイベントをOn Trigger Exitに変更します。
またAnimatorのSet BoolでOpenedのチェックを外すようにし、カスタムイベントはDoorCloseに変更します。
DoorScriptステートグラフに戻り、Open状態を選択し、右クリックからMake Transitionを選択し、Close状態を押します。
OpenからCloseへの遷移条件をダブルクリックし、中身を以下のようにします。
カスタムイベントのDoorCloseを受け取ったらOpenからCloseへと遷移するようにします。
DoorにState Machineを取り付けDoorScriptを設定する
DoorScriptが出来たので、ヒエラルキーでDoorゲームオブジェクトを選択し、インスペクタのAdd ComponentからVisual Scripting→State Machineを選択して取り付け、GraphにDoorScriptを設定します。
実行して確認する
機能が出来たので確認をしますが、その前にキャラクターにはPlayerタグを設定しておきます。
キャラクターを移動させドアに近づくとドアが開き、離れるとドアが閉まります。
上のようになりました。
終わりに
Visual ScriptingのScript Graphで作成すると慣れていないせいかフローポートをどう繋げばいいかわからなくなりますね。(^_^;)