Jump to content
Sign in to follow this  
Jhonatan00_00

ANSWERED Colisiones con OnCollision o RayCast?? (SOLUCIONADO)

Recommended Posts

Buenas noches, viendo un tutorial de Unity para seguir aprendiendo y añadiendo cosas a mi proyecto, he visto que el desarrollador usa RayCast para detectar por ejemplo si el personaje toca el suelo y hacerlo salta, lo que me ha dejado un poco desconcertado porque tenia entendido que RayCast se usaba mas bien para que la cámara no chocara contra las paredes o para la trayectoria de una bala, etc, pero para detectar el suelo siempre había visto que usaban OnCollision.

- ¿Que diferencia hay entre estas dos formas de detectar colisiones?.

- ¿Cual de las dos consume mas recursos?.

No entiendo por que crean dos formas de hacer lo mismo cuando con una sobra y deduzco que no son exactamente lo mismo pero no le veo el razonamiento.

un saludo y gracias.

Edited by Jhonatan00_00
  • Like 1

Share this post


Link to post
Share on other sites

Diferencias

Usas raycasting cuando quieres saber a qué distancia o qué objeto impacta con un un segmento de línea. Así sabemos qué tenemos delante en la dirección de interés.

Ese segmento lo defines en el momento de calcular el raycast, así que puede ser lo que tu quieras (pero por cada rayo que calculas, incurres en un coste computacional).

Se pueden usar para:

  • Calcular la distancia al suelo desde un punto (rayo hacia abajo) para posicionar objetos o cámaras.
  • Calcular el lugar donde impacta un disparo "instantáneo" (rayo desde el cañón de un arma).
  • Calcular en qué objeto está apuntando el ratón.
  • Calcular qué objetos "ve" uno de nuestros personajes o vehículos (se suelen calcular unos cuantos rayos alrededor para saber si vamos a colisionar con algo).

Fíjate que puedes proyectar rayos para calcular la distancia a objetos con los que aún no se está colisionando. Y recuerda también que además del Raycast, tienes el Spherecast y otras variantes, porque una línea es muy fina,  y a veces necesitamos proyectar una "esfera" para saber si hay algún objeto en la dirección de interés.

Las colisiones en Unity en cambio son resueltas por el motor durante la simulación física, y necesitan que haya un Rigidbody involucrado (ya que los Rigidbody son los objetos con los que trabaja el motor físico). Sólo obtendrás colisiones entre Ridigbody y objetos que tengan un Collider de algún tipo. 

Las colisiones se pueden gestionar como eventos, así que Unity llamará a OnCollide cuando se produzca una colisión. Además, los Collider pueden marcarse como "Trigger", lo que causará que no se comporten como sólidos, sino que puedan ser atravesados (en este caso, Unity llamará a OnTriggerEnter).

Esto se puede usar para saber cuando uno de nuestros objetos físicos impacta con algo o atraviesa un "trigger".:

  • Saber cuando un personaje u objeto entra en determinado área.
  • Reproducir sonidos en las colisiones entre objetos.
  • Saber cuándo un personaje u objeto toca a otro o una parte del escenario.

Cuál usar para un character controller

Parece que no es muy recomendable usar el motor físico para controlar un personaje a no ser que la simulación del movimiento en sí misma deba ser físicamente realista. Yo en general opino parecido, pero cada caso varía.

El motivo principal de no usar el motor físico, en mi opinión, para esto, es que tendrías que modelar el movimiento del personaje usando fuerzas, rozamientos, materiales... y gestionar los tropiezos y caídas (superficies inclinadas, etc). Esto puede tener sentido en algunos proyectos pero en general complica lograr un movimiento consistente.

Por otra parte usar uno o unos pocos Raycast es más rápido que dejar que el motor físico mueva tu personaje, aunque esto no tiene un gran impacto si el collider de tu personaje es una simple cápsula.

 

Edited by J Montes
  • Like 2

Share this post


Link to post
Share on other sites

Buenas tardes, estoy haciendo pruebas y creo que RayCast es mucho mas efectivo que Rigidbody para el control del personaje. Para físicas a objetos como por ejemplo para hacer rodar una pelota o caerse un arbol si funciona bien el RigidBody, pero hay cientos de casos en los que el RigidBody complica la programación del personaje.

Sin ir mas lejos tengo a mi personaje programado con un RigidBody y si estoy delante de una pared y salto hacia delante, lo que debería de hacer es saltar hacia arriba (porque hacia delante no puede), pero lo que hace es empujar contra la pared y al actuar las físicas no salta y se queda clavado en el suelo. Esto con RayCast es muy fácil de solucionar, porque simplemente haciendo que cuando tenga objetos a menos de la distancia frontal que indiquemos no avance, saltará siempre hacia arriba cuando esté pegando cara a otro objeto.

Estoy indagando para ver si el RayCast también es mejor para colisiones con enemigos, etc, pero creo que voy a cambiar toda la programación asociada a mi personaje protagonista para que funcione mucho mejor usando RayCast en lugar de RigidBody y me gustaría saber que piensan, para no perder el tiempo haciéndolo y que al final no me sirva para nada.

EDITO -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Por lo que veo también es necesario usar Rigidbody aunque el personaje no se mueva por medio de físicas y es porque para detectar colisiones con paredes y que no las traspase el RayCast se hace mucho mas complejo, ya que tendriamos que lanzar multitud de RayCast y que programar el comportamiento según el caso.

La mejor solución es usar RigidBody para detectar colisiones y que el objeto no traspase las paredes, pero para saber si toca el suelo para saltar o si toca una pared, etc es mucho mejor usar RayCast, al menos 2, uno hacia abajo y otro hacia el frente si tu personaje no puede retroceder de espaldas.

Saludos!!.

Edited by Jhonatan00_00

Share this post


Link to post
Share on other sites

Te recomiendo un tutorial básico que explica cómo implementar y solventar los problemas que te enfrentas al implementar un sistema ("CLASICO") de colisión.

 

  • Like 1

Share this post


Link to post
Share on other sites

Sabia que tendría que hacerlo con Raycast. :2_grimacing:

Ya no por los problemas que he mencionado antes (que con RigidBody también los he podido solucionar después), si no por otros que me hacían el trabajo mucho mas tedioso. Por ejemplo para detectar si colisionaba con una pared o con el suelo tenia que llamar a los objetos 3D "Suelo" y "Pared" y si lo que teníamos delante era una caja, la parte en horizontal de la caja se llamaba "Suelo" mientras que las partes en vertical se llamaban "Pared", así el personaje sabia si podía saltar o no, porque estuviese encima de la caja o chocando contra ella.

Tenia que separar en partes todos los objetos de los niveles para que funcionasen las colisiones correctamente con OnCollision. Ahora con RayCast simplemente le indicas que cuando tenga debajo un objeto (el que sea) está tocando "Suelo" y cuando tenga delante otro objeto (el que sea también), está tocando "Pared" y no tengo que preocuparme a la hora de modelar, de partir los objetos para asignar nombre a cada parte... que alivio... cuanto trabajo me he quitado solo por aprender esto a tiempo. :7_sweat_smile:

Gracias!!.

Share this post


Link to post
Share on other sites
12 hours ago, Jhonatan00_00 said:

Buenas tardes, estoy haciendo pruebas y creo que RayCast es mucho mas efectivo que Rigidbody para el control del personaje. Para físicas a objetos como por ejemplo para hacer rodar una pelota o caerse un arbol si funciona bien el RigidBody, pero hay cientos de casos en los que el RigidBody complica la programación del personaje.

No siempre, si a un rb le seteas la velocidad tenés el mismo control que cualquier kinemático (bueno, digamos bastante control). Lo que hace la diferencia son las técnicas de control en general (sobre todo la detección de suelo), cosa que en el kinemático lo tenés que hacer sí o sí.

 

12 hours ago, Jhonatan00_00 said:

Por lo que veo también es necesario usar Rigidbody aunque el personaje no se mueva por medio de físicas y es porque para detectar colisiones con paredes y que no las traspase el RayCast se hace mucho mas complejo, ya que tendriamos que lanzar multitud de RayCast y que programar el comportamiento según el caso.

Exacto. No necesariamente tienen que ser raycast.

12 hours ago, Jhonatan00_00 said:

La mejor solución es usar RigidBody para detectar colisiones y que el objeto no traspase las paredes, pero para saber si toca el suelo para saltar o si toca una pared, etc es mucho mejor usar RayCast, al menos 2, uno hacia abajo y otro hacia el frente si tu personaje no puede retroceder de espaldas.

Depende el método, trabajando con un método o el otro te va a hacer pensar al algoritmo de una manera o de otra:

1) Un personaje kinemático + queries (raycast, boxcast, shperecast, etc)

  • Se realizan queries
  • se determina la posición final
  • se pone al personaje allí.
  • repetir

Es difícil? Puede que sí, y puede que no, necesitas solo 10 minutos para crear un buen personaje kinemático, que probablemente te va a servir para toda la vida. Será complejo? Ni ahí.

2) un personaje dinámico + contactos (on collision enter/stay):

  • Pasó el update físico (aquí se llenan los contacts, luego de la simulación)
  • Recorrés todos los contactos y los guardas en un buffer (si querés los filtras)
  • en el frame que viene te metés a FixedUpdate (antes de la simulación)
  • Usas los contactos como quieras y determinás una velocidad final
  • Borrás el buffer!!
  • repetir

 

TIP: Si vas a usar contacts, una cosa muy interesante (que nunca me había dado cuenta antes) es que OnCollisionEnter/Say/Exit se corre tantas veces como objectos haya(excluyendo al personaje):

Untitled.jpg

8 hours ago, Jhonatan00_00 said:

Tenia que separar en partes todos los objetos de los niveles para que funcionasen las colisiones correctamente con OnCollision

No, podés usar la información de los contactos. Por ejemplo:

ContactPoint[] buffer = new ContactPoint[20];


void OnCollisionStay( Collision collision )
{
	int contactHits = collision.GetContacts( buffer );

	for( int i = 0 ; i < contactHits ; i++ )
	{
    	ContactPoint contact = buffer[i];
                                    
        //...
    }
}

 

Saludos

Edited by lightbug
  • Like 2

Share this post


Link to post
Share on other sites

WOW!!, cuanta información Lightbug. Por eso me encanta este foro, siempre se aprenden cosas nuevas. :6_smile::91_thumbsup:

Voy a desmenuzar como siempre todo poco a poco y a sacar conclusiones. Porque si como dice podemos usar la información de la colisión de OnCollision para detectar si un objeto está debajo o está delante, no necesitamos RayCast para el personaje. Lo que no entiendo es como lo voy a conseguir contando el número de contactos tal y como ha indicado en el código que ha subido...

 

  • Like 1

Share this post


Link to post
Share on other sites

hola

yo suelo hacer siempre las fisicas con raycast (en realidad lo hago con sphere cast)

si sabes donde estaba el player en el frame anterior (guardando el valor) y donde esta ahora, entonces sabes la direccion y la distancia del "rayo".... y con el sphereCast pues ya calculo el choque e incluso el deslizamiento por pared... aunque si es verdad que a veces lo combino con un rigidBody (pero sin gravedad y con los ejesDeRotacion bloqueados)

pero a veces lo hago unicamente con raycast (sphereCast)

  • Like 1

Share this post


Link to post
Share on other sites

Llevo media mañana tratando de hacer que mi personaje detecte cuando toca el suelo o la pared por medio de RayCast pero no hay forma. Cuando quiere lo hace bien y cuando no se desactiva, me explico: El personaje está dentro de una casa con el suelo perfectamente plano y en horizontal, por lo que todos los puntos del suelo deben de estar a la misma distancia ¿verdad?, pues me muevo un poco con el personaje y detecta que toca suelo, pero me paro en otro punto y ya no toca suelo... ¿Por que?.

Debería de funcionar con esto:

Tocando_Suelo = Physics.Raycast (transform.position,  Vector3.down, 0.22f);

¿Como se usa Sphere Cast?, me da errores.

¿Puedo detectar con OnCollision si la colisión viene de debajo o de frente?.

Share this post


Link to post
Share on other sites

Sigo sin entender como hacer funcionar el SphereCast, pero al menos ya me funciona con Raycast. El problema era que la línea que tira el RayCast es invisible y no podía saber si la estaba calculando bien, por lo que he tenido que dibujar la linea con Debug.DrawRay y posteriormente ajustarla a medida para que vaya como tiene que ir. Ahora funciona correctamente.

Lo mas difícil ha sido hacer el RayCast hacia delante, porque con Physics.RayCast la dirección donde debe de mirar la línea era fija y al rotar el personaje no rotaba junto con el, por lo que he tenido que hacerlo así:

Tocando_Pared = Physics.Raycast (transform.position + Vector3.up * 0.3f, transform.TransformDirection (0,0,1), 0.5f);

Ahora funciona, pero a veces falla porque la línea es demasiado fina. Necesito aprender a aplicar el SphereCast y solucionado. :3_grin:

Share this post


Link to post
Share on other sites

sphere cast es como ray cast pero dandole un radio

RaycastHit hit;
if (Physics.SphereCast(origen, radio, direccion, out hit, distancia)) {
    //ha chocado con algo, entonces haces algo
    //porejemplo:
    transform.position = hit.point;
}

la variable "hit" almacena toda la informacion del "impacto": 

-point: el lugar del impacto

-normal: la "normal" del impacto (hacia donde apunta la superficie, sirve porehemplo para saber si es suelo, pared, techo... y para colocar el objeto añejado del punto de impacto... y otras cosas)

-distance: la distancia que ha recorrido el "rayo" antes de chocar (sirve para porejemplo poner el player en el lugar de origem del rayo y hacer que havance esa distancia y entonces el player estara justo el la posicion deseada (al lado de la pared-suelo))

-"hit" puede decirte tbien el nombre del objeto con el que ha chocado y otras muchas cosas...

puedes hacer que compruebe colisiones solo con una capa (layer) especifica... o que ignore una capa especifica

LayerMask layerm = (1<<8);
//comprobar solo contra la capa numero 8
LayerMask layerm = ~(1<<6);
//comprobar contra todas las capas menos la 6
RaycastHit hit;

if (Physics.SphereCast(origen, radio, direccion, out hit, distacia, layerm)){
    //ha chocado con las capas seleccionadas, hacer algo
}

 

  • Like 1

Share this post


Link to post
Share on other sites

Buenos días, ya he conseguido utilizar SphereCast. El error me lo daba porque no había creado la variable Hit. :91_thumbsup:

Para hacer que salte funciona perfecto. El problema me lo da el SphereCast hacia delante (que he tenido que crearlo a parte del SphereCast hacia abajo) y es que me detecta muy bien objetos que el personaje se encuentre de frente, pero lo que no me detecta es cuando estamos dentro de algo, por ejemplo dentro de una casa, las paredes me las ignora. Funcionan las colisiones con el Rigidbody, pero el SphereCast no actúa con las paredes.

¿Esto es normal?. Estoy pensando en crear dos colliders dentro del personaje, uno ponerlo en los pies y otro en el pecho y que sean estos colliders con OnCollision los que me detecten si choco o no con una pared o con el suelo... Si no se puede arreglar esto de que el SphereCast detecte las paredes lo tendré que hacer así...

EDITO -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

SphereCast a veces no funciona y todo depende del radio que se le de a la esfera. He reducido el radio del SphereCast hacia delante a 0.1f y detecta las paredes.

Edited by Jhonatan00_00

Share this post


Link to post
Share on other sites
Sign in to follow this  

×
×
  • Create New...