Menu
CoddyTech

Интерфейсы и TryGetComponent

Часть раздела Архитектура игр в Unity путешествия по Unity на Coddy. Урок 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 объявляет переменную прямо в вызове. TryGetComponent возвращает true и заполняет target, если у объекта есть компонент, реализующий IDamageable; в противном случае метод возвращает false, а target имеет значение null. Метод проверяет только этот объект, но не его родителя и не его дочерние объекты.

GetComponent по-разному сообщает об отсутствии класса и интерфейса. В редакторе вызов GetComponent<Rigidbody>() для объекта без Rigidbody возвращает фальшивый null Unity: выражение == null даёт значение true, но оператор ?. его не распознаёт, и вызов вызывает исключение MissingComponentException. У интерфейса фальшивого null нет: GetComponent<IDamageable>() возвращает обычный null C#, поэтому с ним работают и == null, и ?.. TryGetComponent позволяет избежать этой проблемы в обоих случаях и ничего не выделяет в редакторе, если компонент отсутствует.

challenge icon

Задание

Легко

Напиши Fire() в Laser: один луч, четыре исхода.

  1. Выполни Physics.Raycast от башни вдоль transform.forward на расстояние range. При промахе выведи в журнал 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