UnityのAnimatorはスクリプトから色々な情報を取得する事が出来ます。
今回はスクリプトからAnimatorの現在の状態を取得してアクションを起こせるようにします。
具体的にはマウスの左ボタンを押した時にキャラクターが剣を振るようにした時、剣を振っている間は次の攻撃の反応をさせないようにします。
剣を振っている間もマウスの左ボタンを押してアニメーションパラメータの条件をオンにしてしまうと、1度剣を振った後に移動させようと思ったらもう一度剣を振ってしまうということが起きてしまいます。
コンボのような攻撃を繋げる場合はいいですが一振りだけのアクションだけの場合は連続で攻撃ボタンが反応してしまうのは困ります。
そうならないようにAnimatorの遷移状態を確認して攻撃のアニメーションが終了してから次の攻撃ボタンの受付が出来るようにしましょう。
アニメーターの状態を確認するだけならサンプルのキャラクターやアニメーターコントローラーを作る必要はありませんのでスクリプトの解説まで飛ばしてください・・・(^_^;)
一応、実際に確認出来た方がいいかなと思ってサンプルの作り方も書いています。
Animator Controllerの設定をする
まずは新しくAnimator Controllerを作り状態とパラメータ、遷移を作ります。
↑のようにFloatでSpeed、TriggerでAttackを作成します。
↑のように状態と遷移を作成します。
Idle→WalkはSpeedが0.1より上、Has Exit Timeのチェックを外す
Walk→IdleはSpeedが0.1より下、Has Exit Timeのチェックを外す
Walk→AttackはAttack、Has Exit Timeのチェックを外す
Idle→AttackはAttack、Has Exit Timeのチェックを外す
Attack→IdleはHas Exit Timeにチェックを入れる
という条件にします。
Idle→Attackの状態遷移の条件を見てみると
↑のようにします。
Attack→Idleは
↑のように設定しました。
Animator Controllerの細かい部分に関しては
あたりを参照してください。
キャラクターに移動と攻撃スクリプトを取りつける
キャラクターに移動と攻撃のスクリプトを取りつけます。
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 41 42 43 44 45 46 47 48 49 50 51 52 53 | using UnityEngine; using System.Collections; public class AnimatorStateChara : MonoBehaviour { private Animator animator; private CharacterController cCon; private Vector3 velocity; [SerializeField] private float walkSpeed; void Start () { animator = GetComponent<Animator>(); cCon = GetComponent<CharacterController>(); velocity = Vector3.zero; } void Update () { // 地面に接地してる時は初期化 if(cCon.isGrounded) { velocity = Vector3.zero; var input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); // 方向キーが多少押されている if(input.magnitude > 0f && !animator.GetCurrentAnimatorStateInfo(0).IsName("Attack") ) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity += input.normalized * walkSpeed; // キーの押しが小さすぎる場合は移動しない } else { animator.SetFloat("Speed", 0f); } if(Input.GetButtonDown("Fire1") && !animator.GetCurrentAnimatorStateInfo(0).IsName("Attack") && !animator.IsInTransition(0) ) { animator.SetTrigger("Attack"); } } velocity.y += Physics.gravity.y * Time.deltaTime; cCon.Move(velocity * Time.deltaTime); } } |
キャラクターの移動の処理に関しては
等を参照してください。
攻撃をしている場所の条件部分を見ていきます。
1 2 3 4 5 6 7 8 | if(Input.GetButtonDown("Fire1") && !animator.GetCurrentAnimatorStateInfo(0).IsName("Attack") && !animator.IsInTransition(0) ) { animator.SetTrigger("Attack"); } |
攻撃ボタンを押しているという条件の他に2つの条件を設定しています。
GetCurrentAnimatorStateInfo(レイヤー番号).IsName
まずはGetCurrentAnimatorStateInfo(レイヤー番号).IsNameから見ていきましょう。
!animator.GetCurrentAnimatorStateInfo(0).IsName(“Attack”)
というのはアニメーターコントローラーの0レイヤー(Base Layer)の現在のいる状態がAttackではない時という条件です。
0というのはアニメーターコントローラーのレイヤー番号で
↑の赤い部分を押すと新しいレイヤーを作成出来ます。
作成したレイヤーはドラッグして順番を変える事が出来、1番上にあるのが0番になり、ベースレイヤーとなります。
今回の場合はBase Layerという元々あるレイヤーしか使いませんので0のまま使用します。
レイヤーが1のAttackの状態を見たいならば
1 2 3 | !animator.GetCurrentAnimatorStateInfo(1).IsName("Attack") |
となります。
IsInTransition
1 2 3 | !animator.IsInTransition(0) |
はアニメーターコントローラーの0レイヤーが遷移途中ではないという条件です。
レイヤーの番号に関してはGetCurrentAnimatorStateInfoと同じなので割愛します。
GetCurrentAnimatorStateInfo.IsNameとIsInTransitionの違い
さてGetCurrentAnimatorStateInfo.IsNameとIsInTransitionはどう違うのでしょうか?
Animator Controllerを使い始めのころは違いがわからず困りました・・・(^_^;)
というわけで画像を使って両方を確認してみます。
まずはGetCurrentAnimatorStateInfo.IsName(“Attack”)の状態です。
Attackのアニメーションを再生している時に条件に合致します。
つまり
1 2 3 | !animator.GetCurrentAnimatorStateInfo(0).IsName("Attack") |
の条件はAttackの状態にいない時という条件になります。
次はIsInTransition(0)を見ていきます。
↑のように0レイヤーであるBase Layerの遷移途中が合致します。
つまり
1 2 3 | !animator.IsInTransition(0) |
はアニメーションが別の状態へと遷移する途中ではないという条件になります。
したがって攻撃へと遷移させる条件を読み解けば
攻撃ボタンを押し、Attackアニメーションを再生中でなく、他の状態への遷移をしている時でない
という状態の時に攻撃のアニメーションへと遷移させている事になります。
つまりIdle状態、Walk状態にいる時だけ攻撃ボタンであるマウスの左ボタンを押して攻撃のアニメーションへと遷移します。
終わりに
Animator Controllerの状態遷移や条件でつまづく事もあると思うのであらためてスクリプトからAnimator Controllerの状態の確認について記事にしました。
正直なところ前半のサンプル作成のところはいらなかったかも・・・・。
まぁ実際作って確認した方が忘れないかなと思いまして・・・・(-_-)