18/12/2019
La detección de colisiones es uno de los pilares fundamentales en el desarrollo de videojuegos. Es la mecánica que permite que nuestro mundo virtual se sienta real y reactivo. Desde un personaje que recoge una moneda hasta un proyectil que impacta en un enemigo, todo depende de un sistema de colisiones robusto. En Unity, contamos con varias herramientas poderosas para gestionar estas interacciones, cada una con sus propias fortalezas y casos de uso. En este artículo exhaustivo, exploraremos a fondo los métodos más comunes: los colliders estándar, los triggers (disparadores) y el versátil sistema de Raycasting. Prepárate para desentrañar los secretos de las físicas de Unity y llevar tus proyectos al siguiente nivel.

- Fundamentos de la Colisión: Colliders y Rigidbodies
- Los Eventos de Colisión Clásicos
- ¿Colisión o Disparador? Entendiendo los Triggers
- Raycasting: Lanzando Rayos para Detectar Objetos a Distancia
- Escenarios Avanzados y Solución de Problemas
- Tabla Comparativa: ¿Qué Método Usar?
- Preguntas Frecuentes (FAQ)
Fundamentos de la Colisión: Colliders y Rigidbodies
Antes de sumergirnos en el código, es crucial entender los dos componentes principales que trabajan en conjunto para crear las colisiones: el Collider y el Rigidbody.
- Collider: Es un componente que define la forma de un objeto para las colisiones físicas. No tiene por qué ser la misma forma que la malla visible del objeto. Unity ofrece una variedad de formas primitivas (Box, Sphere, Capsule Collider) y formas más complejas (Mesh Collider). Un objeto con solo un Collider es considerado un objeto estático en el motor de físicas.
- Rigidbody: Este componente le indica a Unity que debe tratar al objeto como un cuerpo físico dinámico. Al añadir un Rigidbody, el objeto comenzará a ser afectado por la gravedad, la fuerza y otras interacciones físicas. Para que los eventos de colisión más comunes (como
OnCollisionEnter) se disparen, al menos uno de los dos objetos que interactúan debe tener un componente Rigidbody.
La regla de oro es: para colisiones dinámicas donde se involucran fuerzas y movimientos realistas, necesitas Rigidbodies. Para objetos estáticos que solo necesitan delimitar un espacio (como paredes o el suelo), un Collider es suficiente.
Los Eventos de Colisión Clásicos
Cuando dos colliders (con al menos un Rigidbody entre ellos) chocan, Unity llama a una serie de funciones especiales en cualquier script que esté adjunto a esos objetos. Estas funciones nos permiten reaccionar al evento.

OnCollisionEnter(Collision collision): Se llama una sola vez, en el primer fotograma en que los dos colliders se tocan.OnCollisionStay(Collision collision): Se llama en cada fotograma mientras los dos colliders permanezcan en contacto.OnCollisionExit(Collision collision): Se llama una sola vez, en el fotograma en que los dos colliders dejan de tocarse.
El parámetro Collision que reciben estas funciones es muy importante, ya que contiene toda la información sobre el choque, como el otro objeto involucrado, los puntos de contacto y la velocidad relativa.
Veamos un ejemplo práctico:
using UnityEngine; public class DetectorDeImpactos: MonoBehaviour { // Esta función se llama cuando este collider/rigidbody ha comenzado a tocar otro rigidbody/collider. void OnCollisionEnter(Collision collision) { // Imprimimos en la consola el nombre del GameObject con el que colisionamos. Debug.Log("¡Colisión detectada con: " + collision.collider.name); } // Esta función se llama una vez por fotograma por cada collider/rigidbody que esté tocando este rigidbody/collider. void OnCollisionStay(Collision collision) { // Comprobamos si el objeto con el que seguimos colisionando tiene un nombre específico. if (collision.collider.name == "CofreDelTesoro") { Debug.Log("¡Todavía estás tocando el cofre!"); } } }¿Colisión o Disparador? Entendiendo los Triggers
A veces no queremos una colisión física sólida, sino simplemente saber cuándo un objeto entra en una zona determinada. Imagina un área que activa una trampa, una zona de curación o la entrada a una nueva habitación. Para estos casos, usamos los Triggers.
Un Collider se convierte en un Trigger simplemente marcando la casilla "Is Trigger" en su componente en el Inspector. Cuando un Collider es un Trigger, no provoca una colisión física (los objetos pueden atravesarlo), pero sigue detectando la superposición con otros colliders. Esto dispara un conjunto diferente de eventos:
OnTriggerEnter(Collider other): Se llama cuando otro Collider entra en el Trigger.OnTriggerStay(Collider other): Se llama en cada fotograma mientras el otro Collider permanece dentro del Trigger.OnTriggerExit(Collider other): Se llama cuando el otro Collider sale del Trigger.
Fíjate que el parámetro aquí es Collider other, dándonos una referencia directa al collider que entró en nuestra zona, en lugar del objeto de colisión más complejo.

using UnityEngine; public class ZonaMagica: MonoBehaviour { // Para que este script funcione, el GameObject debe tener un Collider con "Is Trigger" activado. // Además, el objeto que entra en la zona debe tener un Rigidbody. void OnTriggerEnter(Collider otherObject) { // Imprimimos un mensaje cuando un objeto entra en nuestra zona. Debug.Log("El objeto '" + otherObject.gameObject.name + "' ha entrado en la zona mágica."); } void OnTriggerExit(Collider otherObject) { // Y otro mensaje cuando sale. Debug.Log("El objeto '" + otherObject.gameObject.name + "' ha salido de la zona mágica."); } }Raycasting: Lanzando Rayos para Detectar Objetos a Distancia
El Raycasting es una técnica increíblemente poderosa y eficiente. Consiste en lanzar un rayo invisible desde un punto de origen, en una dirección específica, y comprobar si choca con algún collider en su camino. Es ideal para cosas como la línea de visión de un enemigo, disparos de armas (hitscan), seleccionar objetos con el ratón o verificar si hay suelo debajo de un personaje.
Lanzando un Rayo desde la Cámara (Estilo FPS)
En un juego de disparos en primera persona (FPS), a menudo queremos saber qué hay exactamente en el centro de la pantalla. Podemos lanzar un rayo desde la cámara hacia adelante.
using UnityEngine; public class VisionFPS: MonoBehaviour { public Camera camaraDelJugador; void Update() { // Creamos un rayo que se origina en el centro de la vista de la cámara. Ray rayo = camaraDelJugador.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0)); RaycastHit hitInfo; // Variable para almacenar la información del impacto. // Dibujamos el rayo en la vista de escena para poder depurar (no es visible en el juego). Debug.DrawRay(rayo.origin, rayo.direction * 100, Color.yellow); // Lanzamos el rayo. Si impacta con algo, la función devuelve true. if (Physics.Raycast(rayo, out hitInfo, 100)) { // Si el rayo golpea un collider, imprimimos el nombre del objeto impactado. Debug.Log("El jugador está mirando a: " + hitInfo.collider.gameObject.name); } } }La Información del Impacto: `RaycastHit`
Cuando un `Physics.Raycast` tiene éxito, nos devuelve un montón de información útil a través de la variable `RaycastHit`. Es crucial conocer sus propiedades para sacar el máximo provecho de esta técnica.

Propiedades Clave de RaycastHit
| Propiedad | Descripción |
|---|---|
point | El punto exacto en el espacio 3D (coordenadas del mundo) donde el rayo impactó con el collider. |
normal | Un vector que representa la normal de la superficie en el punto de impacto (es decir, la dirección perpendicular a la cara del polígono que fue golpeada). |
distance | La distancia desde el origen del rayo hasta el punto de impacto. |
collider | Una referencia directa al componente Collider que fue golpeado por el rayo. |
rigidbody | El componente Rigidbody asociado al collider que fue golpeado. Será nulo si el objeto no tiene un Rigidbody. |
transform | El Transform del objeto que fue golpeado. Es un atajo para acceder a hit.collider.transform. |
textureCoord | Las coordenadas UV de la textura principal en el punto de impacto. Útil para efectos visuales como calcomanías de impacto. |
Escenarios Avanzados y Solución de Problemas
A medida que los juegos se vuelven más complejos, también lo hacen nuestros requisitos de colisión.
Manejo de Múltiples Colliders en un Objeto
Imagina un personaje enemigo que tiene diferentes zonas de daño: cabeza, torso y piernas. Un solo collider no es suficiente. ¿Cómo diferenciamos un disparo a la cabeza de uno al cuerpo?
- Opción A: Objetos Hijos y Tags. Puedes crear GameObjects vacíos como hijos del personaje principal (ej. "Cabeza", "Torso"). A cada uno le asignas un Collider (una esfera para la cabeza, una cápsula para el torso) y un Tag único (ej. "HeadshotZone", "BodyZone"). Luego, en tu código de detección de impacto, simplemente compruebas el tag del collider golpeado:
if (hit.collider.CompareTag("HeadshotZone")) { ... }. - Opción B: Referencias Directas. En el script principal del enemigo, puedes declarar variables públicas para cada collider específico y arrastrarlos desde el editor. Esto crea una referencia directa y te permite comparar directamente en el código, lo que a menudo es más rápido que comparar strings (tags).
public class EnemigoComplejo: MonoBehaviour { public SphereCollider cabezaCollider; public CapsuleCollider cuerpoCollider; public void RecibirImpacto(Collider colliderGolpeado) { if (colliderGolpeado == cabezaCollider) { Debug.Log("¡Disparo a la cabeza! Daño crítico."); // Aplicar daño x3 } else if (colliderGolpeado == cuerpoCollider) { Debug.Log("Impacto en el cuerpo. Daño normal."); // Aplicar daño x1 } } } // En el script del arma: if (Physics.Raycast(rayo, out hitInfo, 100)) { EnemigoComplejo enemigo = hitInfo.collider.GetComponentInParent<EnemigoComplejo>(); if (enemigo != null) { enemigo.RecibirImpacto(hitInfo.collider); } }El Problema Común: `NullReferenceException` con Raycast
Un error muy frecuente al usar `Linecast` o `Raycast` es intentar acceder a la información del `hit` sin comprobar primero si el rayo realmente golpeó algo. Si el rayo no impacta en ningún collider dentro de su rango, la función `Physics.Raycast` devuelve `false` y la variable `hitInfo` no se inicializa. Cualquier intento de acceder a `hitInfo.collider` resultará en un error `NullReferenceException`.
La solución es siempre, sin excepción, meter el código que procesa el impacto dentro de un bloque `if`:
Incorrecto:
Physics.Raycast(origen, direccion, out hit, distancia); // ¡ERROR! ¿Y si el rayo no golpeó nada? hit.collider será nulo. if(hit.collider.gameObject == objetoBuscado) { ... }Correcto:
// Primero, lanzamos el rayo y comprobamos si tuvo éxito. if(Physics.Raycast(origen, direccion, out hit, distancia)) { // Solo si el rayo golpeó algo, podemos acceder a hit.collider de forma segura. if(hit.collider.gameObject == objetoBuscado) { // Lógica del impacto } }Tabla Comparativa: ¿Qué Método Usar?
| Método | Caso de Uso Principal | Ventajas | Desventajas |
|---|---|---|---|
| Colliders (OnCollision) | Interacciones físicas realistas: objetos que rebotan, se apilan, ruedan. | Manejado por el motor de físicas, simulación realista, proporciona información detallada de la colisión. | Más costoso en rendimiento. Requiere al menos un Rigidbody. |
| Triggers (OnTrigger) | Detección de zonas: áreas de activación, recogida de ítems, puntos de control. | Muy ligero en rendimiento, no interfiere con el movimiento de los objetos. | No proporciona información física detallada, solo detecta superposición. |
| Raycasting (Physics.Raycast) | Detección a distancia y precisa: disparos, línea de visión, selección de objetos. | Control total sobre el origen, dirección y distancia. Muy eficiente y versátil. | Solo detecta el primer objeto en su camino (las versiones 'All' son más costosas). Representa una línea infinitesimal, no un volumen. |
Preguntas Frecuentes (FAQ)
P1: ¿Por qué mi `OnCollisionEnter` no se activa?
R: Las causas más comunes son: 1) Ninguno de los dos objetos tiene un componente Rigidbody. 2) Uno de los colliders está marcado como 'Is Trigger'. 3) Los objetos están en capas (Layers) que están configuradas para no interactuar entre sí en la Matriz de Colisiones de Físicas (Physics Collision Matrix). 4) El script no está en el mismo GameObject que el Collider/Rigidbody.

P2: ¿Cuál es la diferencia entre `hit.transform` y `hit.collider.transform` en un RaycastHit?
R: En la mayoría de los casos son lo mismo. Sin embargo, si un Rigidbody tiene varios colliders en objetos hijos, `hit.transform` se referirá al Transform del objeto con el Rigidbody, mientras que `hit.collider.transform` se referirá al Transform del objeto hijo específico que contiene el collider que fue golpeado.
P3: ¿Es el Raycasting costoso para el rendimiento?
R: Un solo Raycast es extremadamente rápido. Sin embargo, el coste puede acumularse si realizas cientos de ellos en cada fotograma, especialmente si tienen una distancia muy larga. Para optimizar, siempre limita la distancia del rayo al mínimo necesario y utiliza LayerMasks para que el rayo solo compruebe las capas de objetos que te interesan, ignorando el resto.
Si quieres conocer otros artículos parecidos a Unity: Guía Maestra de Colisiones y Raycast puedes visitar la categoría Juegos.
