RPGのキャラクタ当たり判定の処理

2024年4月16日

的のテリトリーに入ってきたら追いかける、目の前にきたら攻撃開始する、攻撃が当たったら相手にダメージを与えるという一連の処理を考えています

敵から見たプレイヤーとの関係性

3つのコライダーを使って、状態によって、使い分けられています

当たり判定用のコード

共通で使うようにしています
コライダーは別々のオブジェクトにアタッチされています
そうすることで、それぞれのコライダーで個別に当たり判定ができるようになるからです
1つのオブジェクトに複数のコライダーのアタッチはできますが、切り分け(違うイベントを呼び出す)が、できません

using System;
using UnityEngine;
using UnityEngine.Events;

[RequireComponent(typeof(Collider))]
public class CollisionDetector : MonoBehaviour
{
    // トリガーで止まっている状態
    [SerializeField]
    TriggerEvent onTriggerStay = new TriggerEvent();

    // トリガー検出の開始
    [SerializeField]
    TriggerEvent onTriggerEnter = new TriggerEvent();

    // Unityの標準イベント
    void OnTriggerStay(Collider other)
    {
        onTriggerStay.Invoke(other);
    }

    // Unityの標準イベント
    void OnTriggerEnter(Collider other)
    {
        onTriggerEnter.Invoke(other);
    }

    [Serializable]
    public class TriggerEvent : UnityEvent<Collider>
    {
    }
}

第一段階:索敵中(相手を見つけたかどうかの判定)

自分の索敵エリアに何か入ってきたかをSphereColliderのisTriggerStayで判定
Stayなので、このエリアに入っている間中イベントが発生、近寄るように動くアクションをする

コリジョンを検出という意味で「Collision Detector」ゲームオブジェクトにCollisionDetectorコンポーネントがアタッチされています(とどまっている間ということで、On Trigger Stayのイベントを登録

第2段階:目の前に相手がいるか

自分の攻撃エリアに何か入ってきたかをSphereColliderのisTriggerStayで判定
Stayなので、このエリアに入っている間、攻撃(かみつき)アクションをする
第1段階で相手に近寄っていくので、相手が動いていないと必然的にこのエリアに相手が入ってくることになる

攻撃範囲の検出という意味で「Attack Range Detector」ゲームオブジェクトにCollisionDetectorコンポーネントがアタッチされています(とどまっている間ということで、On Trigger Stayのイベントを登録

第3段階:攻撃ができる段階

第2段階のときだけこのコライダーが有効になります
攻撃アニメーションのかぶりつきの時、有効で、かぶりつきを終えた時無効になるパターンを繰り返します

攻撃に移っても相手が目の前にいれば、攻撃ヒットとみなす(ここは、isTriggerEnterで、ヒットした時の1度だけメソッドを実行

攻撃がヒットしたかの検出という意味で「Attack Hit Detector」ゲームオブジェクトにCollisionDetectorコンポーネントがアタッチされています(ヒットすれば、とどまっている必要がないので、On Trigger Enterのイベントを登録

動作中の様子

表示はワイヤーフレームにしています
シーンビューでの攻撃中だけコライダーの有効無効の様子とインスペークターでBoxColliderの有効無効スイッチが繰り返されている様子を確認しましょう

Unityエディタからみる具体的なオブジェクト、コンポーネントの関係

詳細

敵からみてプレイヤーがテリトリーに入って来れば追いかける

自分のテリトリーに入ってくる時に特定の処理を実行したい

敵の周りに大きなコライダーをアタッチして、ここの中にプレイヤーが入ってきたらイベントを発生させます
OnTriggerStayを使いますが、トリガーイベントが発生する処理をドラッグ&ドロップで選択できるような共通のスクリプトを使っています

レイヤー毎による特定のオブジェクトとの当たり判定設定

レイヤー設定がDefaultです
ここでは、プレイヤーとの当たり判定はしていません

EnemyMoveスクリプト


プレイヤーとの当たりを判定するために、CompareTagメソッドを使っています

public void OnDetectObject(Collider collider)
{
    if (collider.CompareTag("Player")){}
}

敵からみてプレイヤーが攻撃範囲にに入って来れば攻撃する

自分の攻撃範囲に入ってくる時に特定の処理を実行したい

敵の前面に小さなコライダーをアタッチして、ここの中にプレイヤーが入ってきたらイベントを発生させます
OnTriggerStayを使いますが、トリガーイベントが発生する処理をドラッグ&ドロップで選択できるような共通のスクリプトを使っています

レイヤー毎による特定のオブジェクトとの当たり判定設定

レイヤー設定がEnemyAttackなので、Playerとだけ当たり判定が実行されます

MobAttackスクリプト

イベントハンドラは次のようになります
1つのメソッドが呼び出されています(直訳すると、もし可能ならアタックする)

public void OnAttackRangeEnter(Collider collider)
{
    AttackIfPossible();
}

攻撃判定

攻撃イベントハンドラ

攻撃アニメーションの動き

攻撃中に敵が範囲内であれば、相手にダメージを与える

自分の攻撃中に特定の処理を実行したい

敵の前面に小さなコライダーをアタッチして、ここの中にプレイヤーが入ってきたらイベントを発生させます
このコライダーは、攻撃アニメーション中に有効になったり無効になったりさせています
OnTriggerStayを使いますが、トリガーイベントが発生する処理をドラッグ&ドロップで選択できるような共通のスクリプトを使っています

レイヤー毎による特定のオブジェクトとの当たり判定設定

レイヤー設定がEnemyAttackなので、Playerとだけ当たり判定が実行されます

MobAttackスクリプト

イベントハンドラは次のようになります

衝突相手のMobStatusスクリプトを取得して、Damageメソッドを呼び出します

public void OnHitAttack(Collider collider)
{
    var targetMob = collider.GetComponent<MobStatus>();
    if (targetMob == null) return;
        
    // プレイヤーにダメージを与える
    targetMob.Damage(1);
}

ダメージを与えるメソッド

フローチャートの代わりに箇条書きとしています(チャート作成にチャレンジしてみてもいいですね)

  1. 状態が「死んでいる」なら、このメソッドは終了
  2. ライフを1減らす
  3. ライフが0より大きければ、このメソッドは終了
  4. 状態を「死んでいる」に変更
  5. 死んでいくアニメーションを実行
  6. 死んだ時のその他処理を実行
public void Damage(int damage)
{
    if (_state == StateEnum.Die) return;

    _life -= damage;
    if (_life > 0) return;

    _state = StateEnum.Die;
    _animator.SetTrigger("Die");

    OnDie();
}

死んだ時の処理メソッド

今は空ですね

protected virtual void OnDie()
{
}

Unity

Posted by hidepon