Menu
CoddyTech

インターフェースとTryGetComponent

CoddyのUnityジャーニー「Unityのゲームアーキテクチャ」セクションの一部。レッスン 8/64。

TryGetComponentは1回の呼び出しでコンポーネントを探し、見つかったかどうかを知らせます。インターフェースにも使えるため、接触した相手がダメージを受けるかどうかを調べる通常の方法です。この地雷は、IDamageableを実装している周囲のすべてのものにダメージを与えます。

public class Mine : MonoBehaviour
{
    public float radius = 3f;
    public int damage = 5;

    void OnTriggerEnter(Collider other)
    {
        foreach (Collider c in Physics.OverlapSphere(transform.position, radius))
        {
            if (c.TryGetComponent(out IDamageable target))
            {
                target.TakeDamage(damage);
            }
        }
        Destroy(gameObject);
    }
}

out IDamageable targetは、呼び出しの中で変数を宣言します。オブジェクトにIDamageableを実装するコンポーネントがある場合、TryGetComponentはtrueを返してtargetに値を設定します。そうでない場合はfalseを返し、targetはnullになります。調べるのはそのオブジェクトだけで、親や子は調べません。

GetComponentは、クラスがない場合とインターフェースがない場合とで異なる結果を返します。Editorでは、Rigidbodyがないオブジェクトに対してGetComponent<Rigidbody>()を呼び出すと、Unityの偽のnullが返されます。== nullはtrueになりますが、?.では検出できず、呼び出しはMissingComponentExceptionをスローします。インターフェースには偽のnullがありません。GetComponent<IDamageable>()は実際のC#のnullを返すため、== nullと?.のどちらも使えます。TryGetComponentを使えばどちらの場合もこの問題を避けられ、Editorではコンポーネントが見つからなくても何も割り当てられません。

challenge icon

チャレンジ

簡単

Laser に Fire() を書きましょう。1本の光線で、4つの結果が起こります。

  1. 塔から transform.forward の方向に、長さ range で Physics.Raycast を飛ばします。外れた場合は Missed をログに出力します。
  2. hit.collider.TryGetComponent(out IDamageable target) の場合は、TakeDamage(damage) を呼び出し、"Hit " + hit.collider.name をログに出力します。
  3. それ以外の場合、Rigidbody があれば、AddForce(transform.forward * push, ForceMode.Impulse) を実行し、名前の前に "Pushed " を付けてログに出力します。なければ、名前の前に "Blocked by " を付けてログに出力します。

「プレイ」を押し、あらゆる方向に向きを変えて発射しましょう。その後、「確認」を押します。

自分で試してみよう

using UnityEngine;
using UnityEngine.InputSystem;

public class Laser : MonoBehaviour
{
    public float range = 10f;
    public int damage = 1;
    public float push = 5f;
    public float turnSpeed = 90f;

    private InputAction moveAction;

    void Start()
    {
        moveAction = InputSystem.actions.FindAction("Move");
    }

    void Update()
    {
        float turn = moveAction.ReadValue<Vector2>().x;
        transform.Rotate(0f, turn * turnSpeed * Time.deltaTime, 0f);
        if (Keyboard.current.spaceKey.wasPressedThisFrame)
        {
            Fire();
        }
    }

    void Fire()
    {
        // 前方に長さがrange単位のレイを飛ばし、当たった対象を処理する
    }
}

Unityのゲームアーキテクチャのすべてのレッスン

自分で練習してみよう: Unityプレイグラウンド