Unity: 攻撃のダメージ処理

Unity入門 - Project Bonfire

攻撃が当たって敵にダメージが入る処理を作っていきます。

今回は、攻撃側のコライダーが1個、ダメージを受ける側のコライダーが1個で作成します。それぞれ多数付けるのは、後日書きます。

はじめに

攻撃の当たり判定には、Rigidbody と Collider を使用します。Collision は、Layer Collision Matrix を使って必要な Collision を絞ります。

実装

Layer の追加と Layer Collision Matrix の設定

Layer の追加から行います。

Project Settiings を開き、Tags and Layers を選択します。

そこに、以下のように Layer を追加します。

追加が終わったら、Project Settings 内の Physics に移動します。そして、以下のように Layer Collision Matrix を設定します。

Player キャラクターの設定

Knight プレファブを開いて、hipsRigidbody, BoxCollider, HitArea.cs スクリプトを追加します。パラメータは以下のようにします。

  • Layer を PlayerHit にする
    • ※ このとき、すべての子オブジェクトにも Layer を設定するか聞かれるので、「No, this object only」を選択してください
  • BoxCollider の IsTrigger にチェックを入れる
  • BoxCillider のサイズを調整する
  • Ridgidbody の Is Kinematic にチェックを入れる

Layer 設定時の選択肢です。間違えないでください。間違えた場合は一度 Default に戻して子オブジェクトも Default にした後やり直してください。

Rigidbody と Collider を使った当たり判定の説明の詳細は省略します。ググってもらえばたくさん見つかると思います。以下、概要です。

  • Is Trigger にチェックを入れると Collider が通り抜けられるようになる。
  • Ridgidbory の Is Kinematic にチェックを入れると他からの物理的な影響を受けなくなる。
  • TriggerEnter が発行されるためには、Collider が付いているどちらかに Rigidbody が付いている必要がある。

自分で試してみたこと。

  • BoxCollider と Hit Area のスクリプトは同じ GameObject についている必要がある。
  • Rigidbody は root の GameObject についていても問題なさそう。とにかく1つ Rigidbody が付いていれば良いと思う。

BoxCollider は次のような感じに付いています。

hips への追加が終わったら、Knight 直下に AttackAreaActivator.cs スクリプトを追加します。

武器の設定

WeaponMaul 内の Maul にコライダーを付けます。WeaponMaul のプレハブを開きます。そのなかの Maul に Sphere ColliderAttackArea.cs スクリプトを取り付けます。Collider は、Box Collider や Capsule Collider でも構いません。形状が合うものを適宜選んでください。

Layer を PlayerAttack にします。
※ このとき、すべての子オブジェクトにも Layer を設定するか聞かれるので、「No, this object only」を選択してください

Is Trigger のチェックは忘れないようにしてください。

取り付けるとこんな感じになります。

敵の準備

アセットストアから敵キャラをダウンロードしてインポートします。

今回は、このキャラにしました。

testscene.unity 以外は全てインポートします。

インポートが終わったら、いつものように Asset Packs フォルダに移動しておきます。

paladin さんの prefab を Scene画面内に ドラッグアンドドロップします。

Character Controller と PlayerStatus.cs を取り付けます。Animator の Controller に何もしないで立っているアニメーションが設定されているので、Animator はこのまま使います。Character Controller のパラメータは画像の通りです。PlayerStatus (Script) の DebugText は、後で HUD に新しいデバッグ用のエリアを追加てから取り付けます。

ここまでできたら paladin を一度自分用にプレハブ化しておきます。Game/Character というフォルダを作成し、そこに paladin をドラッグアンドドロップしてプレハブ化します。

続いて、プレイヤーキャラクターと同様に hips に Layer を設定し、Box Collider, Rigidbody, HitArea スクリプトを追加します。ここでも Layer 設定時に子以下のオブジェクトへの反映はしないようにしてください、

Box Collider を付けたところはこんな感じになります。

HUD の追加

攻撃した際に、正しくダメージが入っているかどうか確認するために、HUD に 確認用のText を追加します。

HUD のプレハブを開き、PlayerMoveText の下に、EnemyText を追加します。フォントを12px, フォントの色を 白にしました。

HUDへの追加終わったら、今作成した EnemyText を paladin の PlayerStatus (Script) の Debug Text に 設定します。

追加設定

敵のHP を表示できるように PlayerStatus.cs が新しくなっているので差し替えるます。

設定関連は、以上です。

実行して敵を攻撃したら、敵のHPが減っていくはずです。

動作説明

処理の流れ

武器を振ってからダメージが入るまでの処理は次のようになっています。

  1. [初期化処理] Start() 時に AttackAreaActivator が武器についている Collider を検索してキャッシュします。その際に Collider を無効化します。
  2. 武器を振ると 攻撃アニメーションについているイベントから StartAttackHit() が呼び出されます。これは、AttackAreaActivator.cs で処理され、武器についている Collider を有効化します。
  3. 特に何も当たらなければ、攻撃アニメーションについているイベントからEndAttackHit() が呼び出されます。これは、AttackAreaActivator.cs で処理され、武器についている Collider を無効化します。
  4. 2で衝突が起こる場合。Layer Collision Matrix で設定したように、PlayerAttack と EnemyHit は衝突するとトリガが起動されます。
  5. HitArea.cs 側にはトリガの処理は入っていません。
  6. AttackArea.cs 側の OnTriggerEnter で、誰がどのくらいのダメージの攻撃したという情報を、イベントシステムのメッセージシステムを使って衝突した側に送ります。
  7. 送られてきたイベントを HitArea.cs の OnDameged で受け取ります。受け取ったらダメージを受けた側の PlayerStatus の HP を減らします。

メッセージシステム

参考にした書籍では SendMessage を使っていましたが、ちょっと調べたらイベントシステムのメッセージシステムを使った方法の方がいいよって書いてあったので、そちらを使いました。こちらの方が新しいらしい。

使い方です。

まず、インタフェースを定義します。今回は IAttackDamane としました。インタフェースは IEventSystemHandler を継承する必要があります。

using UnityEngine.EventSystems;

namespace Bonfire.Combat
{
    //IEventSystemHandlerを継承させる
    public interface IAttackDamage : IEventSystemHandler
    {
        void OnDamaged(AttackInfo attackInfo);
    }
}

次に、受け取り側です。インタフェースを継承し、インタフェースで定義したメソッドを実装します。

    public class HitArea : MonoBehaviour, IAttackDamage
    {
        PlayerStatus status = null;

        private void Start()
        {
            status = transform.parent.GetComponent<PlayerStatus>();
        }

        public void OnDamaged(AttackInfo attackInfo)
        {
            // Debug.Log("get damage");
            status.HP -= attackInfo.weaponDamage;
        }
    }

最後に、呼び出し側です。ExecuteEvents.Executeで、受け取り側の OnDamaged 関数に AttackInfo を引数に渡して呼び出します。このとき、引数は複数指定できます。

using UnityEngine.EventSystems;
<中略>

            ExecuteEvents.Execute<IAttackDamage>(
                target: other.gameObject,
                eventData: null,
                functor: (reciever, eventData) => reciever.OnDamaged(attackInfo)
            );

ファイル

Combat フォルダは、Scripts フォルダの下においてください。

PlayerStatus.cs は修正を加えていますので、差し替えてください。

以下修正点など。

  • フォルダを作成してファイルを置いたので、namespace を Bonfire.Combat とした
  • 敵の方は攻撃手段を用意していないので、ActivateAttackArea.cs を付けていない

動かないときは

次のようなところを確認してください。

  • スクリプトが全部付いていること、それぞれが正しい位置に付いていることを確認する
  • Rigidbody の IK にチェックが入っている
  • Collider の Is Trigger のチェックが入っている
  • Collider がついているGameObject の Layer が正しく設定されている
  • Layer Collision Matrix が正しく設定されている
  • コメントアウトしてある Debug.Log を有効にして、メッセージが渡っていることを確認する

おわりに

スクリプトの説明を書こうかと思ったのですが、メッセージシステムを使っているだけなので、特に書くことはありませんでした。

以上で、攻撃のダメージ処理ができました。