インターフェースを使用した衝突判定の実装方法

インターフェースとは

具体的な実装を持たずに、抽象的なメソッドを宣言するものです。これはクラスに実装され、多重継承が可能です。

インターフェースのメリット

具体的な実装を持たないため、処理の抽象化が可能になり、同じ処理をする場合でも状況に応じて分岐させることができます。
ゲーム開発においては衝突判定が当てはまります。衝突する物体に対して振る舞う処理を変えたい場合にインターフェースを介して、抽象的な処理を挟むことによって、汎用性の高い実装が可能になります。

インターフェースの実装例

今回はアクションゲームでよく見られる、敵の攻撃をプレイヤーが受けた場合の処理をインターフェースを用いて記述していきます。
Unityの衝突判定と言えば、タグを使用することが私は多かったですが、タグを使用してしまうと、敵の攻撃対象がプレイヤーに固定されてしまって、プレイヤー以外に攻撃した場合はif文をまた別で書く必要があり、汎用性に欠けます。このような場合に、インターフェースは便利なのでその実装例を書いていきます。

設計の例

今回は以下のような設計で実装を進めます。敵の攻撃を管理するEnemyAttackControllerからIDamagebleインターフェースを実装したPlayerDamageControllerに対してプレイヤーへの攻撃を呼び出して、PlayerHealthManagerで体力減少の処理を行います。

実装していく設計は以下の通りです。

EnemyAttackController

using UnityEngine;

public class EnemyAttackController : MonoBehaviour
{
    [SerializeField] private int power = 1;

    private void OnCollisionEnter2D(Collision2D collision)
    {
        //IDamagebleインターフェースを実装しているオブジェクトなら
        if (collision.gameObject.TryGetComponent(out IDamageble damageble))
        {
            damageble.TakeDamage(power);
        }
    }
}

攻撃対象のGameObjectがIDamagebleインターフェースを実装しているかを確認して、実装している場合は攻撃処理を呼び出します。

IDamageble

public interface IDamageble
{
    void TakeDamage(int damage);
}

インターフェースの実装です。interfaceキーワードをクラス名の前に記述することで定義できます。
ここに抽象メソッドのTakeDamageを宣言します。

PlayerDamageController

using UnityEngine;

public class PlayerDamageController : MonoBehaviour, IDamageble
{
    [SerializeField] private PlayerHealthManager playerHealthManager;

    public void TakeDamage(int damage)
    {
        playerHealthManager.ApplyDamage(damage);
    }
}

ここでプレイヤーがダメージを受けた際の具体的な処理を記述します。本来ならばヒットストップなどの処理も必要ですが、今回は体力を減らす処理をPlayerHealthManagerから呼び出す処理のみ記述します。

PlayerHealthManager

using UnityEngine;
using UniRx;

public class PlayerHealthManager : MonoBehaviour
{
    [SerializeField] private int maxHealth = 100;
    private ReactiveProperty _health;

    private void Awake()
    {
        _health = new ReactiveProperty(maxHealth);
    }

    //外部から購読できるようにする
    public IReadOnlyReactiveProperty Health => _health;

    //ダメージを受けたらHealthを減らす
    public void ApplyDamage(int damage)
    {
        _health.Value -= damage;
    }
}

ここで実際にプレイヤーの体力管理して、体力を減らす処理を記述します。

PlayerHealthUI

using UnityEngine;
using TMPro;
using UniRx;

public class PlayerHealthUI : MonoBehaviour
{
    [SerializeField] private PlayerHealthManager playerHealthManager;
    [SerializeField] private TextMeshProUGUI healthText;

    private void Start()
    {
        playerHealthManager.Health
            .Subscribe(health => healthText.text = $"HP: {health}");
    }
}

補足ですが、PlayerHealthManagerの体力状態を購読して、プレイヤーの体力UIを
更新する処理を記述しています。

オブジェクトにスクリプトを割り当てる

  • PlayerのゲームオブジェクトにPlayerHealthManagerとPlayerDamageController
  • EnemyのゲームオブジェクトにEnemyAttackController
  • 空のゲームオブジェクトにPlayerHealthUI

実行結果

プレイヤーが敵に触れるとダメージを受けて、UIが更新されます。また、Playerにはタグを設定することなく、処理が実行できました。

まとめ

今回はインターフェースを用いて衝突判定の処理を実装してみました。
これ以外にもゲーム制作では抽象的な処理を導入することで、汎用性が高くなり、急な仕様変更にも対応しやすくなります。
今後も様々な設計を学んで取り入れていこうと思います。

最新情報をチェックしよう!
>
CTR IMG