Интерфейсы и TryGetComponent
Часть раздела Архитектура игр в Unity путешествия по Unity на Coddy. Урок 7 из 50.
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 по-разному сообщает об отсутствии класса и отсутствии интерфейса. В Editor вызов GetComponent<Rigidbody>() для объекта без Rigidbody возвращает фальшивый null Unity: == null возвращает true, но ?. его не распознаёт, и вызов выдаёт MissingComponentException. У интерфейса фальшивого null нет: GetComponent<IDamageable>() возвращает настоящий C# null, поэтому с ним работают и == null, и ?.. TryGetComponent позволяет избежать этой проверки в обоих случаях и ничего не выделяет в Editor, если компонент отсутствует.
Задание
ЛегкоЛазерная башня поворачивается с помощью стрелок влево и вправо, а пробел вызывает Fire() в Laser.cs. Напиши Fire. Выполни лучевой запрос от башни в направлении её передней оси длиной range единиц (в Inspector задано 10), затем:
- Если луч попал во что-то, у чего есть IDamageable, вызови для него
TakeDamage(damage)и выведи в журналHitи имя объекта, напримерHit Robot. - Если у объекта нет IDamageable, но есть Rigidbody, оттолкни его с помощью
AddForce(transform.forward * push, ForceMode.Impulse)и выведи в журналPushedи его имя. - Во всех остальных случаях выведи в журнал
Blocked byи имя объекта, напримерBlocked by Pillar. Если луч ни во что не попал, выведиMissed.
Используй TryGetComponent для обеих проверок. IDamageable.cs, Robot.cs и Balloon.cs уже готовы. Нажми Play и стреляй во всех направлениях, затем нажми Check.
Попробуйте сами
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
1Компоненты, а не иерархии
Небольшие компонентыБазовый скрипт врагаАбстрактные компонентыСовместная работа компонентовПовторение: модульная турель4Сериализованные данные
Сериализуемые классыСписки в InspectorПеречисления в InspectorПовторение: таблица лута2Интерфейсы в Unity
IDamageableИнтерфейсы и TryGetComponentGetComponentsПовторение: клавиша взаимодействия5ScriptableObjects
Ресурсы данныхХарактеристики оружияОбщие настройкиВолны как данныеПовторение: база предметов