はじめに
こんにちは、ソーシャルゲーム事業部のUnityエンジニアのアファトです。 この記事はカヤックUnityアドベントカレンダー2016の15日目の記事になります。
ゲームにおいて、モーションに合わせるロジックはいろいろな方法で実装できますが、場合によって、実装しづらいや調整しにくいケースがあります。そのために Unity 5 から導入された State Machine Behaviour という仕組みがあります。アニメーションロジック または アニメーションに近い・依存するロジックをこの仕組みを利用することで、実装しやすくなり、メンテナンスもしやすくなります。
State Machine Behaviourについて
State machine behavioursはMecanimで作られたAnimationのロジックをよりコントロールしやすくするため、Animator Controllerの中にあるanimator stateやsub-state machinesにアタッチできるscriptです。StateのinspectorのAdd Behaviourボタンを押すと State Machine Behaviour (SMB) が利用できます。
新しいScriptを作る場合、以下の様なテンプレートができます。
using UnityEngine; using System.Collections; public class NewStateMachineBehaviour : StateMachineBehaviour { // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state //override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { // //} // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks //override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { // //} // OnStateExit is called when a transition ends and the state machine finishes evaluating this state //override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { // //} // OnStateMove is called right after Animator.OnAnimatorMove(). Code that processes and affects root motion should be implemented here //override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { // //} // OnStateIK is called right after Animator.OnAnimatorIK(). Code that sets up animation IK (inverse kinematics) should be implemented here. //override public void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { // //} }
そこにStateMachineBehaviourを継承して、五つ関数をoverrideします:
- OnStateEnter stateに入るときに一回呼ばれます。初期化のためなど
- OnStateUpdate stateの中に行うUpdateはこの関数で対応できます
- OnStateExit stateが完了する時、ほかのstateに移動するとき一回呼ばれます。clean upのためなど
- OnStateMove root motionに関するロジックはこの関数で対応できます
- OnStateIK Inverse Kinematics に関するロジックこの関数で対応できます
この五つ関数は全部同じ引数が渡されます: animator
、animatorStateInfo
、layerIndex
- animator はアタッチされるstate machine behaviourのAnimatorのコンポーネントの参照です。このコンポーネントに
GameObject
の参照があるので MonoBehaviour とのやり取りができます。 - animatorStateInfo は現状 state の状態の情報です。animation clip の長さに対する正規化した time などの情報が入ってます。
- layerIndex は state machine behaviour のstateのレイヤーの情報。ベースレイヤーだと 0 になります。
State Machine Behaviourで出来ること
State に依存するロジックなどは SMB で実装しやすくなります。以下の Animator Controller で SMB ができることを試しに作ってみましょう。
5つ states: Charge
→ Jump
→ Attack
→ Return
→ End
というアニメーションの流れで、End
以外の state に入る時に Sound Effect (SE) を再生できる SMB を作ります。
using UnityEngine; using System.Collections; public class SoundEffectPlayerSMB : StateMachineBehaviour { public AudioClip soundEffectClip; public float delay; private float elapsedTime; private AudioSource audioSource; // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { if (null == audioSource) { // 例えば AudioSource というマネージャーコンポーネントがあったら var audioSourceObj = GameObject.Find("AudioSourceManager"); audioSource = audioSourceObj != null ? audioSourceObj.GetComponent<AudioSource>() : null; } if (null != soundEffectClip && 0 >= delay) { PlaySoundClip(); } } // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { if (null == soundEffectClip) { return; } elapsedTime += Time.deltaTime; if (elapsedTime >= delay) { PlaySoundClip(); } } private void PlaySoundClip() { if (null == audioSource) { return; } if (null == soundEffectClip) { return; } audioSource.PlayOneShot(soundEffectClip); soundEffectClip = null; } }
上記にの SMB script ができたら、state ごとにアタッチすると、その state に入る時に設定した audio clip と delay で SE が再生してくれます。
おわりに
State に依存するロジックは SMB script で実装しやすいし、メンテナンスもしやすくなりますので、結構便利に使えると思います。
明日は UI アニメーションなどによく使われる Tweening についての話になります。担当は高です。お楽しみに〜