今回はUnityのレイヤーとアバターマスクを使ってキャラクターの一部のアニメーションを変化させてみようと思います。
例えば普通に手を振って歩いている状態のアニメーションがあって、下半身にはこの普通の歩いているアニメーションを使い、上半身は物を持っている為に手は振らないようにするという事を行います。
体の一部分だけのアニメーションを変更したい場合はAnimatorControllerのレイヤーとアバターマスクを使う事で実現出来ます。
歩いているアニメーションは今までの歩いているアニメーションのままで、上半身用に別のアニメーションを用意します。
上半身用のアニメーションは手の部分しか使わないので他の部分のアニメーションはどういう風に設定されていても構いません。
今回の記事では以前作成した機能を修正している個所があります。
今までの記事の流れは
で確認出来ます。
新しいレイヤーの作成とアバターマスクの設定
まずは新しいレイヤーを作成します。
AnimatorControllerでLayersを選択し+をクリックすると新しいレイヤーが作成されます。
新しいレイヤーの名前をUpper Layerという名前にします。
次にAvatarMaskを作成します。ProjectタブのAssetsフォルダ内でCreate→AvatarMaskを選択します。
AvatarMaskのインスペクタでHumanoidの人型の絵をクリックし腕の部分だけ緑色にします(画像ではIKが赤になっていますが、手のIKも緑にした方がいいかも?)。
このアバターを先ほど作ったレイヤーに設定すると緑色の部分だけのアニメーションが適用されるレイヤーということになります。
次にUpper Layerの右の歯車をクリックします。
上のようなウインドウが出るのでWeightを1、アバターをさきほど作ったアバターにします。
BlendingはOverrideにします。
これで普段のアニメーションへ上書きする事になります。
Sync(Syncronizeの略だと思われる、意味は同期する)にチェックを入れるとSourceレイヤーの選択項目が出ます。
Sourceレイヤーには普段のアニメーションをコントロールしているレイヤーを選択します。
Upper LayerにはSourceレイヤーで作成したステートの遷移が表示されます。
Upper Layerの歩くアニメーションステートWalkを選択し、そのモーションに手の動きを使いたいアニメーションを設定すれば、完成です。
上のように手以外の部分は普段のアニメーションが再生され、手の部分だけUpper LayerのWalkステートのアニメーションが再生されています。
静止画なのでわかりにくいですが・・・・。
これで歩いている時に部分的にアニメーションを上書きする事が出来ました。
立っている時、走っている時、攻撃時で上半身の動きを変更する
で連続攻撃が出来るようになりました。
そこで、ただ立っている時、走っている時は上半身のアニメーションを別のものに変更し、攻撃時は最初に設定したアニメーションを再生するように設定してみます。
上の動画がBase Layerに設定した走るアニメーションです。
走っている時の手の向きの関係で剣がキャラクターにめり込んで走ってしまってます。
動作に問題はありませんが・・・、剣を自分にめり込ませながら走る主人公なんて見たくありません(^_^;)
そこで、新しいレイヤーを作り、立っている時と走っている時は上半身のアニメーションを変更します。
Handという新しいレイヤーを作り、アバターには上半身だけを設定したHandAvatarを指定します。
Blendingは元のアニメーションを上書きして再生させる為にOverrideにし、Syncにチェックを入れてBase Layerの状態遷移を表示します。
遷移は上のようになっており、HandレイヤーにBase Layerレイヤーと同じ状態遷移が表示されます。
Syncにチェックを入れると指定したレイヤーと同じ状態遷移がそのレイヤーにも表示されますが、アニメーションクリップがNoneになっているので、そこに上書きしたいアニメーションクリップを指定します。
とりあえず剣がキャラクターを切りつけないような上半身のアニメーションを指定します。
StandardAssetの3rdPersonで使っているHumanoidJumpForwardRightを指定してみます。
今回の場合、上半身のアニメーションを変更したいのは立っている時と走っている時なので、
HandレイヤーのIdleとRunのアニメーションクリップにHumanoidJumpForwardRightを設定します。
これで設定が完了しました。
実行して確認してみましょう。
闘牛士のような少し違和感のあるアニメーションを設定してしまいましたが (^_^;)
立っている時と走っている時は上半身がHumanoidJumpForwardRightのアニメーションを再生し、攻撃やジャンプをした時はHandレイヤーのAttack系にアニメーションを設定していないので、Base Layerレイヤーのアニメーションがそのまま再生されています。
これでずいぶんゲームのキャラクター作りが出来上がってきました!
アクションRPGの主人公キャラクターを作る時に困らないぐらいの機能は出来てきたかも!?
一部のアニメーションを条件付きで上書きする方法
レイヤーとアバターを使ってアニメーションの一部を別のものに替える事が出来るようになりました。
しかし、例えば武器を切り替えた場合、武器も懐中電灯の時と同じように懐中電灯を持って歩いているアニメーションになってしまいます。
武器は別の持ち方にしたいですよね?
同じ状態にいる時でも武器によってアニメーションを上書きしたい時はSyncにチェックをいれるのではなく、レイヤーを作ったらOverrideにし、アバターはさきほどと同じ物を使用します。
そしてアニメーションの遷移を独自に作ります。
上のような感じでIdleからWalkFlashLightに遷移を繋げ、さらにExitに持っていきます。
Idleにはモーションクリップの設定は行わず単なる遷移の為のステートとします。
Idle状態がデフォルトの状態で、アニメーションパラメータのFlashLightがOnになったらWalkFlashLightに遷移するという条件を加えます。
FlashLightがOffになったらExitに遷移させます。Exitは遷移するとデフォルトのステートに移動します。
アニメーションパラメータのFlashLightは持っている持ち物がFlashLightであればOnにし、そうでなければOffにするというスクリプトの処理が必要になります。
Onにするのは歩き始めた時で、Offにするのは持ち物を持ちかえた時、歩くのをやめた時です。
アニメーターをanimator変数に取得していたならば、
animator.SetBool(“FlashLight”, true);
animator.SetBool(“FlashLight”, false);
という処理をそれぞれの条件に加えればOKです。
これでアニメーターコントローラーのレイヤーとアバターを使って、部分的にアニメーションを変化させる事が出来るようになります。
アニメーションの上書きのサンプル
以前の記事で作った武器を切り替える機能を取り付けている場合は、
アニメーションパラメータにBool型のHaveSwordを作成し、Idleにはアニメーションクリップを設定しないでHaveSword状態では剣を持った時のアニメーションクリップを設定します。
遷移条件はHaveSwordがOnになった時で条件のHas Exit Timeのチェックを外しておきます。
武器を切り替えるタイミングの所(上の記事ではChangeEquipスクリプト)で武器が剣だった時にアニメーションパラメータのHaveSwordをtrueにします。
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 | void InstantiateWeapon() { equipment++; if (equipment >= weapons.Length) { equipment = -1; } // 今装備している武器を削除 if (equipTransform.childCount != 0) { Destroy(equipTransform.GetChild(0).gameObject); } // 素手ではない時だけ武器をインスタンス化 if (equipment != -1) { // 新しく装備する武器をインスタンス化 var weapon = Instantiate<GameObject>(weapons[equipment]); processCharaAnimEvent.SetCollider(weapon.GetComponent<Collider>()); // サンプルの為、直接武器の位置や角度を設定 if (equipment == 0) { weapon.transform.SetParent(equipTransform); weapon.transform.localPosition = new Vector3(0.002f, 0.035f, 0.065f); weapon.transform.localEulerAngles = new Vector3(315f, 99f, 264f); weapon.transform.localScale = new Vector3(0.03f, 0.1f, 0.03f); } else { weapon.transform.SetParent(equipTransform); weapon.transform.localPosition = new Vector3(0.004f, 0.029f, 0.065f); weapon.transform.localEulerAngles = new Vector3(345f, 275f, 84f); weapon.transform.localScale = new Vector3(1f, 1f, 1f); } if(weapon.CompareTag("Sword")) { animator.SetBool("HaveSword", true); } myStatus.SetEquip(weapon); } else { animator.SetBool("HaveSword", false); } } |
上の例では武器を切り替えた時に武器のプレハブをインスタンス化するメソッドですが、武器をインスタンス化する時に武器のゲームオブジェクトのタグがSwordだった時にアニメーションパラメータのHaveSwordをtrueにしています。
素手の時はHaveSwordをfalseにしています。
ただこのままだと例えばアニメーターコントローラーでAttack状態へと移行した時も上半身のアニメーションはUpper Layerのアニメーションで上書きされているので攻撃時も上半身はずっとHaveSwordで指定したアニメーションになってしまいます。
そこでAttack状態にBehaviourスクリプトを取り付けてこれを回避する事にします。
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 54 55 56 57 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class HaveSwordBehaviour : StateMachineBehaviour { private MyStatus myStatus; private void OnEnable() { myStatus = FindObjectOfType<MyStatus>(); } // 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 (myStatus != null) { var weaponStatus = myStatus.GetWeaponStatus(); if (weaponStatus != null && weaponStatus.GetWeaponType() == WeaponStatus.WeaponType.Sword) { animator.SetBool("HaveSword", false); } else { animator.SetBool("HaveSword", true); } } } // 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) { if (myStatus != null) { var weaponStatus = myStatus.GetWeaponStatus(); if (weaponStatus != null && weaponStatus.GetWeaponType() == WeaponStatus.WeaponType.Sword) { animator.SetBool("HaveSword", true); } else { animator.SetBool("HaveSword", false); } } } // OnStateMove is called right after Animator.OnAnimatorMove() //override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) //{ // // Implement code that processes and affects root motion //} // OnStateIK is called right after Animator.OnAnimatorIK() //override public void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) //{ // // Implement code that sets up animation IK (inverse kinematics) //} } |
上のスクリプトでは主人公がMyStatusスクリプトが設定されており、MyStatusで装備している武器のゲームオブジェクトを保持している前提です。
Attack状態に遷移した時に装備している武器がSwordタイプの武器だったらアニメーションパラメータのHaveSwordをfalseにします。
Attack状態から出ていくときにSwordタイプの武器だったらHaveSwordをtrueにします。
他にもやり方はあると思いますが、今回はこれにしました。
これで武器を切り替えて剣だった時だけ他のアニメーションに切り替わります。
終わりに
アニメーターコントローラーのレイヤーとアバターマスクを使う事で体の部位に合わせて上書きするアニメーションを設定する事が出来るのは便利ですね!
次回はアニメーターコントローラにAdd Behaviourしてビヘイビアでアニメーションの処理をしてみます。