今回は武器を装備したり外したりできるようにします。また、武器を装備しているときと、装備を外しているときで別のアニメーションになるようにします。
アニメーション周りの実装
パラメータの追加
装備を付けたり外したりするアニメーションを実行するためのフラグを追加します。Bool 型で EquiptWeapon と UnequipWeapon を追加しました。
Animator の設定
アニメーションを追加します。2Hand-Sword-Unsheath-Back-Unarmed を使うのですが、名称が長いので Equip-Weapon としました。
同様に 2Hand-Sword-Unsheath-Back-Unarmed を Unequip-Weapon として追加します。
それぞれの Transition は次のようにしています。Locomotion から Equip-Weapon へは、Has Exit Time のチェックを外して、Conditions に EquipWeapon = true を入れます。タイミングはあまりシビアでなくていいので、左にずらすことはしませんでした。このあたりは、お好みで。
Equip-Weapon から Locomotion への戻りは、Has Exit Time で戻ります。
Unequip-Weapon は、Equip-Weapon と同様に設定します。
アニメーションの確認
今まで作成した攻撃やローリングのアニメーション同様に、アニメーションが終了するタイミングで関数呼び出しを追加するのですが、最初から WeaponSwitch というのが付いていますので、これを使用することにします。
Animator Override Controller の作成
装備を外したときのアニメーションを追加するために、Animator Override Controller を使用します。元の Animator Controller に対して、変更したいアニメーションを指定することができます。
さっそく追加します。
名所を UnarmedAnimatorOverrideController としました。
Inspector に移動します。
まず、Controller にもとになる Animator Controller を設定します。今回は、プレイヤーキャラクターに設定してある PlayerAnimatorController です。
Controller を設定したら、差し替えるアニメーションを設定します。2Hand-Sword-Sheath-Back-Unarmed と 2Hand-Sword-Unsheath-Back-Unarmed、あとBasicMotions のところは None のままにします。Attack1, Attack4 などは好きなモーションを選んでください。
アニメーションの設定
以前、攻撃やローリングを作成したときと同様に、アニメーションの長さの調整やイベントの設定を行います。
以下のように Project 画面から直接アニメーションを選択して設定していきます。
Unarmed-Roll-Forward は、ローリング後の待機時間が長いため、時間を短くしました。それと、終了間際にCallRollingEnd を呼ぶようにイベントを設定します。
Unarmed-Attack-R2は、実行時間は問題なかったっためイベントだけ設定します。以下のように、StartAttackHit と EndAttackHit と CallAttackR1End を 設定します。
Unarmed-Attack-L3 も同様です。こちらは、終了時のイベントで呼び出すのは CallAttackR1_2End です。
パンチの作成
武器の1種としてパンチを作成します。
R_HandWeapon の下で Create Empty を実行して空の GameObject を作成します。そしてそれをWeaponPunch という名称にします。
WeaponPunch に以下の設定を行います
- Layer を PlayerAttack にする
- SphereCollider を取り付ける
- SphereCollider の IsTrigger にチェックを入れる
- SphereCollider 位置とサイズを設定する。これはお好みで。この設定は少し大きめにしてあります。
- AttackArea.cs を取り付ける
ここまで設定したらプレハブ化します。以下のように PlayerCharacter の下に配置しました。プレハブ化が終わったらKnight の R_HandWeapon から WeaponPunch は削除してください。
プレハブ内を見てみるとSphere Collider しかない状態です。
スクリプトの取り付けとプレハブの設定
まず、Weapon.cs を Knight に取り付けます。
Weapon (Script) の Animator Override ~ WeaponHandPunchPrefab をそれぞれ画像のように設定してください。
スクリプトの差し替え
スクリプトを今回添付したものに差し替えてください。
ファイルは記事の末尾に添付してあります。
以上で、武器の付け外しと攻撃アニメーションの変更ができ、パンチで攻撃するとパンチでも敵にダメージが入るはずです。パンチは右だけにしか攻撃判定を付けていないため、左での攻撃では敵にダメージが入りません。
左の攻撃もダメージが入るようにするためにはもうひと手間必要なので、今回は、つけませんでした。2段目の攻撃も右手にしてしまえば、2段目の攻撃でもダメージが入るようになります。
武器の装備を付けたり外したりするのは、十字キーの右ボタンです。
スクリプトの説明
スクリプトの簡単な説明です。
PlayerStatus.cs
変数に、EquipWeapon と UnequipWeapon を追加しています。
//状態
public bool isGrounded = false;
public bool isAttackR1 = false;
public bool isAttackR1_2 = false;
public bool isRolling = false;
public bool isJumping = false;
public bool isDied = false;
public bool EquipWepon = false;
public bool UnequipWeapon = false;
PlayerAnimation.cs
まず、今回作成した Wepon クラスを呼び出せるようにします。
Weapon weapon = null;
<中略>
void Start()
{
animator = GetComponent<Animator>();
status = GetComponent<PlayerStatus>();
weapon = GetComponent<Weapon>();
}
EquipWeapon と UnequipWeapon の終了処理である WeaponSwitch() を実装します。フラグをリセットする前に、武器の装備処理 (※後述) を呼び出して、武器の着脱を行います。
void WeaponSwitch()
{
if(status.EquipWepon)
{
weapon.WeponEquipment(true);
}
if(status.UnequipWeapon)
{
weapon.WeponEquipment(false);
}
status.resetStatus();
}
あとは、EquipWeapon と UnequipWeapon の設定をする処理を追加しています。
void Update()
{
animator.SetBool("IsGrounded", status.isGrounded);
animator.SetBool("IsAttackR1", status.isAttackR1);
animator.SetBool("IsAttackR1_2", status.isAttackR1_2);
animator.SetBool("IsRolling", status.isRolling);
animator.SetBool("IsJumping", status.isJumping);
animator.SetBool("EquipWeapon", status.EquipWepon);
animator.SetBool("UnequipWeapon", status.UnequipWeapon);
}
PlayerCtrl.cs
十字キーの入力 と装備の切り替え
十字キーは、Axis として認識されます。実際に来る値は 0 と 1のはずです。以下のように、canInputDpadRightをフラグにして、1が来たら次は0がくるまで入力を受け取らないようにしました。
十字キーを使って武器を装備しているかどうかのフラグを切り替え、次の状態遷移を決めます。
// 十字キーの入力用フラグ
bool canInputDpadRight = true;
<中略>
// 武器を装備しているかどうかのフラグ
bool isEquipWeapon = true;
<中略>
if (canInputDpadRight && Mathf.Approximately(dPad, 1f))
{
if (isEquipWeapon)
{
nextState = State.UnEquipWeapon;
}
else
{
nextState = State.EquipWeapon;
}
isEquipWeapon = isEquipWeapon ? false : true;
canInputDpadRight = false;
}
if (Mathf.Approximately(dPad, 0f))
{
canInputDpadRight = true;
}
State とそれに関連するメソッドの追加
武器装備と装備解除の State を追加します。
public enum State
{
Died,
Walking,
AttackR1,
AttackR1_2,
Rolling,
Jump,
EquipWeapon,
UnEquipWeapon
};
これに関連して Switch に、StartEquipWeapon(), StartUnEquipWeapon() の処理を追加し、それぞれの関数を作成しました。
// 次の状態に遷移できるようになった時の処理
if (isStateEnd)
{
State prevState = state;
// StartXX 内で nextState が書き換えられるので、ここで state を更新しておく必要がある
state = nextState;
switch (nextState)
{
case State.Walking:
StartWalking(prevState);
break;
<中略>
case State.EquipWeapon:
StartEquipWeapon(prevState);
break;
case State.UnEquipWeapon:
StartUnEquipWeapon(prevState);
break;
}
}
private void StartEquipWeapon(State prevState)
{
StateStartCommon();
status.EquipWepon = true;
}
private void StartUnEquipWeapon(State prevState)
{
StateStartCommon();
status.UnequipWeapon = true;
}
Weapon.cs
新規に追加したクラスです。武器関連の処理を扱うクラスです。
変数の定義
AnimatorOverrideController と武器のプレハブ、武器の位置を以下のような変数で持ちます。
// 武器の装備関連
[SerializeField] AnimatorOverrideController animatorOverride = null;
[SerializeField] GameObject weaponBackPrefab = null;
[SerializeField] GameObject weaponHandMaulPrefab = null;
[SerializeField] GameObject weaponHandPunchPrefab = null;
Transform weaponBackPosition = null;
Transform weaponHandPosition = null;
武器を取り付ける場所を取得
Start() 時に、武器を持つ位置を取得します。背中の位置と右手の位置をそれぞれ名前で検索します。
void Start()
{
animator = GetComponent<Animator>();
status = GetComponent<PlayerStatus>();
playerMove = GetComponent<PlayerMove>();
hudController = FindObjectOfType<HudController>();
weaponBackPosition = findTransformInChildren(transform, "BackWeaponPosition");
weaponHandPosition = findTransformInChildren(transform, "R_HandWeapon");
}
// name で指定した オブジェクトを探すメソッド
Transform findTransformInChildren(Transform tf, string name)
{
Transform ret = null;
if (tf.name == name)
{
return tf;
}
for (int i = 0; i < tf.childCount; i++)
{
ret = findTransformInChildren(tf.GetChild(i), name);
if (ret != null)
{
return ret;
}
}
return null;
}
武器の廃棄
武器の廃棄メソッドは次のようにしています。武器の付け替えの際は、背中についているものと右手に持っているものを両方 Destroy するように書いてあります。実際には、どちらか一方にしか武器は付いていないはずですので、Destroy が呼ばれるのは1回です。
private void DestroyOldWeapon()
{
if (weaponHandPosition.childCount > 0)
{
Transform oldWeapon = weaponHandPosition.GetChild(0);
if (oldWeapon != null)
{
oldWeapon.parent = null;
Destroy(oldWeapon.gameObject);
}
}
if (weaponBackPosition.childCount > 0)
{
Transform oldBackWeapon = weaponBackPosition.GetChild(0);
if (oldBackWeapon != null)
{
Destroy(oldBackWeapon.gameObject);
}
}
}
武器の装備
コメントに書いてある通りです。
public void WeponEquipment(bool isArmed)
{
DestroyOldWeapon();
// 武器を装備する
if (isArmed && weaponHandMaulPrefab != null)
{
Instantiate(weaponHandMaulPrefab, weaponHandPosition);
}
// 武器を背中に付ける
if (!isArmed && weaponBackPrefab != null)
{
Instantiate(weaponBackPrefab, weaponBackPosition);
}
// 拳に当たり判定を付ける
if (!isArmed && weaponHandPunchPrefab != null)
{
Instantiate(weaponHandPunchPrefab, weaponHandPosition);
}
// 攻撃側の当たり判定の再設定
// ※ここでやろうとしたが、Destroy や Instantiage の実際の処理は後で行われる
// ようなので、AttackArea を生成した時に呼ぶように変更した。
// attackAreaActivator.SetAttackArea();
// アニメーションの差し替え
var overrideController = animator.runtimeAnimatorController as AnimatorOverrideController;
if (!isArmed && animatorOverride != null)
{
animator.runtimeAnimatorController = animatorOverride;
}
else if (overrideController != null)
{
animator.runtimeAnimatorController = overrideController.runtimeAnimatorController;
}
}
ファイル
おわりに
武器の着脱とアニメーションの変更が思ったより簡単にできました。AnimatorOverrideController って便利ですね。
次回、x ボタンだけでダッシュとローリングとジャンプをできるようにします。