Menu
CoddyTech

接口与 TryGetComponent

本课属于 Coddy Unity 学习旅程的“Unity 游戏架构”单元。第 8 课,共 64 课。

TryGetComponent 会在一次调用中请求组件,并告诉你是否找到了组件。它也适用于接口,因此通常用来询问你碰到的对象是否会受到伤害。这枚地雷会对周围所有实现了 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. 从塔的位置沿着 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 在线编辑器