Interfaces y TryGetComponent
Parte de la sección Arquitectura de juegos en Unity del Journey de Unity de Coddy. Lección 7 de 50.
TryGetComponent solicita un componente y te indica si lo encontró, en una sola llamada. También funciona con interfaces, por lo que suele ser la forma habitual de preguntar al objeto con el que has chocado si puede recibir daño. Esta mina daña a todo lo que tiene a su alrededor que implementa 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 declara la variable dentro de la llamada. TryGetComponent devuelve true y asigna un valor a target cuando el objeto tiene un componente que implementa IDamageable; de lo contrario, devuelve false y target es null. Solo busca en ese objeto, nunca en su padre ni en sus hijos.
GetComponent informa de forma distinta si falta una clase o una interfaz. En el Editor, GetComponent<Rigidbody>() en un objeto sin Rigidbody devuelve el null falso de Unity: == null es true, pero ?. no lo detecta y la llamada lanza MissingComponentException. Una interfaz no tiene un null falso: GetComponent<IDamageable>() devuelve un null real de C#, así que tanto == null como ?. funcionan con él. TryGetComponent evita esta diferencia en ambos casos y no asigna memoria en el Editor cuando falta el componente.
Desafío
FácilLa torre láser gira con las flechas izquierda y derecha, y la barra espaciadora llama a Fire() en Laser.cs. Escribe Fire. Lanza un rayo desde la torre en la dirección hacia la que apunta, de range unidades de longitud (el Inspector establece 10), después:
- Si alcanza algo que tenga un IDamageable, llama a
TakeDamage(damage)y registraHity el nombre del objeto, como enHit Robot. - Si el objeto no tiene un IDamageable, pero sí un Rigidbody, empújalo con
AddForce(transform.forward * push, ForceMode.Impulse)y registraPushedy su nombre. - Cualquier otra cosa registra
Blocked byy su nombre, como enBlocked by Pillar. Si el rayo no alcanza nada, registraMissed.
Usa TryGetComponent para ambas búsquedas. IDamageable.cs, Robot.cs y Balloon.cs ya están listos. Pulsa Play y dispara en todas las direcciones; después, pulsa Check.
Pruébalo tú mismo
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()
{
// Lanza un rayo hacia adelante, de range unidades de longitud, y gestiona lo que alcance
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Arquitectura de juegos en Unity
1Componentes, no jerarquías
Componentes pequeñosUn script base para enemigosComponentes abstractosComponentes que trabajan juntosRepaso: torreta modular4Datos serializados
Clases serializablesListas en el InspectorEnumeraciones en el InspectorRepaso: tabla de botín7Máquinas de estados
Estados como clasesUna máquina de estadosTransicionesCerebros de enemigosRepaso: estados de puertas10Proyecto: Superviviente de la arena
El héroeTipos de enemigos2Interfaces en Unity
IDamageableInterfaces y TryGetComponentGetComponentsRepaso: tecla de interacción5ScriptableObjects
Recursos de datosEstadísticas de armasConfiguración compartidaOleadas como datosRepaso: base de datos de objetos