Интерфейсы и 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 позволяет избежать этой проблемы в обоих случаях и ничего не выделяет в редакторе, если компонент отсутствует.
Задание
ЛегкоНапиши Fire() в Laser: один луч, четыре исхода.
- Выполни
Physics.Raycastот башни вдольtransform.forwardна расстояниеrange. При промахе выведи в журналMissed. - Если
hit.collider.TryGetComponent(out IDamageable target), вызовиTakeDamage(damage)и выведи в журнал"Hit " + hit.collider.name. - Иначе, если у объекта есть
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
1Компоненты, а не иерархии
Небольшие компонентыБазовый скрипт врагаАбстрактные компонентыКомпоненты работают вместеПовторение: модульная турельПовторение: спаренная пушка4Сериализованные данные
Сериализуемые классыСписки в инспектореПеречисления в инспектореПовторение: таблица добычиПовторение: определяем добычу2Интерфейсы в Unity
IDamageableИнтерфейсы и TryGetComponentGetComponentsПовторение: клавиша взаимодействияПовторение: ближайший в пределах досягаемости5ScriptableObjects
Ресурсы данныхХарактеристики оружияОбщие настройкиСохраняем ветерВолны как данныеПовторение: база предметовПовторение: покупка в магазинеПотренируйтесь самостоятельно: Песочница Unity