Interfaces and TryGetComponent
Part of the Game Architecture in Unity section of Coddy's Unity journey. Lesson 8 of 64.
TryGetComponent asks for a component and tells you whether it found one, in one call. It works with interfaces too, which makes it the usual way to ask whatever you hit whether it can be hurt. This mine hurts everything around it that is 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 declares the variable inside the call. TryGetComponent returns true and fills target when the object has a component that implements IDamageable; otherwise it returns false and target is null. It looks only at that one object, never its parent or children.
GetComponent reports a missing class and a missing interface differently. In the Editor, GetComponent<Rigidbody>() on an object with no Rigidbody returns Unity's fake null: == null is true, but ?. does not see it and the call throws MissingComponentException. An interface has no fake null: GetComponent<IDamageable>() returns a real C# null, so == null and ?. both work on it. TryGetComponent avoids the question for both, and it allocates nothing in the Editor when the component is missing.
Challenge
EasyWrite Fire() in Laser: one ray, four outcomes.
- Cast
Physics.Raycastfrom the tower alongtransform.forward,rangelong. On a miss, logMissed. - If
hit.collider.TryGetComponent(out IDamageable target), callTakeDamage(damage)and log"Hit " + hit.collider.name. - Else, if it has a
Rigidbody,AddForce(transform.forward * push, ForceMode.Impulse)and log"Pushed "plus the name; else"Blocked by "plus the name.
Press Play, turn and fire every way. Then Check.
Try it yourself
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()
{
// Cast a ray forward, range units long, and deal with what it hits
}
}
All lessons in Game Architecture in Unity
1Components, Not Hierarchies
Small ComponentsA Base Enemy ScriptAbstract ComponentsComponents Working TogetherRecap: Modular TurretRecap: Twin Cannon4Serialized Data
Serializable ClassesLists in the InspectorEnums in the InspectorRecap: Loot TableRecap: Roll the Loot2Interfaces in Unity
IDamageableInterfaces and TryGetComponentGetComponentsRecap: Interact KeyRecap: Nearest in ReachPractice on your own: Unity playground