[TUTORIAL] Coding: Simple pero útil. 01 - Colisiones

Esta es el primer tutorial que hago y espero que ayude a la gente que está empezando a programar, y también a los que ya han empezado. Creo que siempre viene bien saber otras formas de hacer las cosas.

Hoy empezaré con algo simple, que parece fàcil de primeras, pero que si no se hace bien acaba saliendo una bazòfia injugable (suponiendo que estés creando un videojuego).


Colisión de Circulos
Empezaremos utilizando objetos que se puedan mover y se necesite saber constantemente si estan o no colisionando 2 objetos.
Por ejemplo, tenemos al señor Pacman y una Bola:
Imagen

Como podeis ver, he añadido en el dibujo el radio de cada circulo y la distancia entre sus respectivos centros.
Ahora bien, se os ocurre alguna forma de ver si los 2 circulos estan colisionando? Si os dais cuenta, tratandose de esferas, únicamente al mover las esferas cambiará la distància entre estas, rotando la imagen (mentalmente) puedes llegar al mismo problema, véase en la siguiente imagen:
Imagen
Como podeis ver, por mucho que movamos de coordenadas X i Y los 2 circulos, se puede simplificar el problema y ver que se necesitan tan solo 3 valores para comprobar si colisionan o no:
-Radio circulo 1 (Pacman)
-Radio circulo 2 (bola que come pacman(?))
-Distancia entre circulos

Dando como resultado el siguiente problema:
Imagen
Ahora ya tenemos todo listo para ver de qué manera podremos ver si estas 2 esferas colisionan o no, lo haremos de la siguiente manera:
Una forma de pensar "Como diablos puedo ver como colisionan?" es pensar justamente todo lo contrario: como sabemos que no colisionan seguro?
Fijaos que si acercamos la esfera blanca (linea blanca) hacia pacman (linea amarilla), colisionaran cuando la distancia entre estos sea menor a la suma de los 2 radios:
colisiona: distancia < radio1 + radio2
no colisiona: distancia > radio1 + radio2
O lo que es lo mismo:
Colisiona si se cumple la siguiente condición: Distancia - radio2 < radio1
Imagen
En esta imagen vemos el caso extremo que depende de cada uno si considera que es colisión o no lo és: dist - radio2 = radio1
Claramente vemos que si la bola blanca se acerca a pacman, la medida "distancia - radio2" disminuye y obviamente están colisionando.
(Si teniamos 'dist - radio2 = radio1' con 'dist - radio2' disminuyendo, obtenemos 'dist - radio2 < radio1', logicamente)

Por lo tanto, ya tenemos nuestro sistema para saber si 2 circumferencias están colisionando o no, dando las coordenadas XY de cada circulo con su radio, calcularemos la distancia entre ellos:
bool colision(int x1, int y1, int radio1, int x2, int y2, int radio2) {
    int distancia = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); //<--- Teorema de Pitagoras
    return (distancia - radio2 < radio1);
}


o:
return (distancia < radio1 + radio2)

como os guste mas.



Colisión de Rectángulos
Hemos visto que en el problema de las circumferencias que lo podiamos simplificar a 1D (una dimension). Pues bien, la colisión de rectángulos es exactamente lo mismo pero en 2D.
Para ver si colisionan o no 2 rectangulos es un poco más difícil de imaginarselo. Una manera MUY facil es la siguiente:

Sabremos que no colisionará si:
1-la parte derecha del rectangulo 1 se encuentra mas a la izqueirda que la parte izquierda del rectangulo 2
2-la parte izquierda del rectangulo 1 se encuentra mas a la derecha que la parte derecha del rectangulo 2
3-la parte de abajo del rectangulo 1 se encuentra mas arriba que la parte de arriba del rectangulo 2
4-la parte de arriba del rectangulo 1 se encuentra mas abajo que la parte de abajo del rectangulo 2
imagen:
Imagen

Sabemos que el sistema de coordenadas de la NDS sitúa al eje de origen (0,0) a la parte posterior izquierda (arriba-izquierda) de la pantalla. Haciendo calculos, obtenemos lo siguiente (para cualquier rectangulo, con sus x, y, ancho, altura correspondiente):
parte derecha = x + ancho
parte izquierda = x
parte arriba = y
parte abajo = y + alto


Tenemos la siguiente equación (para ver si no colisionan):
bool colision(int x1, int y1, int ancho1, int alto1, int x2, int y2, int ancho2, int alto2) {
    if (x1 + ancho1 < x2) return false;     //parte derecha (Rect 1) situada mas a la izquierda que parte izquierda (Rect 2)
    if (x1 > x2 + ancho2) return false;     //parte izquierda (Rect 1) situada mas a la derecha que parte derecha (Rect 2)
    if (y1 + alto1 < y2) return false;       //parte abajo (Rect 1) situada mas arriba que parte arriba (Rect 2)
    if (y1 > y2 + alto2) return false;       //parte arriba (Rect 1) situada mas abajo que parte abajo (Rect 2)
    return true;  //no se cumple ninguna de las anteriores: entonces colisiona!
}


Podemos ajuntarlo todo en una sola linea:
bool colision(int x1, int y1, int ancho1, int alto1, int x2, int y2, int ancho2, int alto2) {
    return not ( (x1+ancho1 < x2) or (x1 > x2+ancho2) or (y1+alto1 < y2) or (y1 > y2+alto2) );
}

Nota: fijaos en el NOT, que niega la formulita de ver si no-colisionan, por lo que si negamos tal formula obtenemos la de 'colisionan?'

Aplicando la lógica, vemos que la anterior equacion es equivalente a:
bool colision(int x1, int y1, int ancho1, int alto1, int x2, int y2, int ancho2, int alto2) {
    return (x1+ancho1 > x2) and (x1 < x2+ancho2) and (y1+alto1 > y2) and (y1 < y2+alto2);
}

Por lo que obtenemos 4 condiciones de saber si colisionan:
1-parte derecha (R1) mas a la derecha que parte izquierda (R2)
2-parte izquierda (R1) mas a la izquierda que parte derecha (R2)
3-parte de abajo (R1) mas abajo que parte de arriba (R2)
4-parte de arriba (R1) mas arriba que parte de abajo (R2)
Un poco mas "dificil" de verlo de esta forma, verdad? [+risas]

Nota para Colisiones en Objetos 3D
Supongo que queda claro que una esfera (circulo 3D y tal..) seria practicamente la misma formula que la de 2D, verdad? (Piensalo y lee el spoiler)
Tan solo tenemos la coordenada 'z' de más, que la necesitaremos para calcular la distancia:
distancia = raiz((x2-x1)² + (y2-y1)² + (z2-z1)²);


Y los rectangulos mas de lo mismo, añadir la Z donde toca:
bool colision(int x1, int y1, int z1, int ancho1, int alto1, int hondo1, int x2, int y2, int z2, int ancho2, int alto2, int hondo2) {
    return not ( (x1+ancho1 < x2) or (x1 > x2+ancho2) or (y1+alto1 < y2) or (y1 > y2+alto2) or (z1+hondo1 < z2) or (z1 > z2+hondo2) );
}


Fácil, no? XD
Utilizando el mismo método, podreis conseguir mirar si colisionan 2 objetos en 2D, 3D, .. 4D! (no se como imaginarlo, pero viendo tal funcion es fácil, no? ;P)
imagina que la 4a dimension es el tiempo, y piensa tus paranoyas :P







Pues bien esto es todo por hoy, cuando vuelva a tener tiempo haré la segunda parte de las colisiones:
-Colisiones en Mapas (tipo RPG, tactics, etc.)
-Colision Perfecta (MUY MUY (EXTREMADAMENTE) lenta)

Espero que os sirva de algo, almenos para entender mejor de donde salen estas formulitas :P
[tadoramo] [tadoramo] [tadoramo] [tadoramo]

Muy buen tuto si señor.
gracias :)

no parece que interese demasiado :<
Buen tuto, éstas cosas son bastante útiles, ya que se usan continuamente (lo de 3D no tanto).
Muchas gracias, esta muy bien explicado todo [oki].
Muchas gracias, seguro que me servirá de ayuda cuando empieze a programar a mediados de agosto.
Una pregunta: En el Palib, que es con lo que pienso programar (creo que es el mas sencillo, vosotros avisadme si no es así)
¿Se puede crear gran cantidad de elementos en vista diseño? Quiero decir: Se va viendo lo que haces, o para que quede bien debes estar SIEMPRE haciendo todo desde el código?¿No hay vista diseño?
Gracias!

Y en serio, buent tuto. [plas]
lamateporunyogur escribió:Muchas gracias, seguro que me servirá de ayuda cuando empieze a programar a mediados de agosto.
Una pregunta: En el Palib, que es con lo que pienso programar (creo que es el mas sencillo, vosotros avisadme si no es así)
¿Se puede crear gran cantidad de elementos en vista diseño? Quiero decir: Se va viendo lo que haces, o para que quede bien debes estar SIEMPRE haciendo todo desde el código?¿No hay vista diseño?
Gracias!

Y en serio, buent tuto. [plas]


El método bueno es por código. Han sacado un Game Maker, pero ni lo he probado :P. Los conversores de gráficos son visuales, eso sí. De todos modos, no es muy complicado cargar gráficos. Ya lo verás.
ANTONIOND escribió:El método bueno es por código. Han sacado un Game Maker, pero ni lo he probado :P. Los conversores de gráficos son visuales, eso sí. De todos modos, no es muy complicado cargar gráficos. Ya lo verás.


He visto el Game Maker, pero es complicadísimo.
Veremos eso de que no es muy complicado, pero gracias, yo en cuanto veo una líneas de código me pongo a tmblar [mad] [mad] , y eso que le tengo unas ganas. [babas]

EDIT: Dices que el método bueno es por código, pero: ¿Tiene vista diseño no?
lamateporunyogur escribió:
ANTONIOND escribió:El método bueno es por código. Han sacado un Game Maker, pero ni lo he probado :P. Los conversores de gráficos son visuales, eso sí. De todos modos, no es muy complicado cargar gráficos. Ya lo verás.


He visto el Game Maker, pero es complicadísimo.
Veremos eso de que no es muy complicado, pero gracias, yo en cuanto veo una líneas de código me pongo a tmblar [mad] [mad] , y eso que le tengo unas ganas. [babas]


Nah... ya verás como en cuanto hagas cualquier tontería y llenes la pantalla de líneas de código le pierdes el miedo ;)
lamateporunyogur escribió:EDIT: Dices que el método bueno es por código, pero: ¿Tiene vista diseño no?

El Game Maker tiene vista diseño. XD
ANTONIOND escribió:
lamateporunyogur escribió:EDIT: Dices que el método bueno es por código, pero: ¿Tiene vista diseño no?

El Game Maker tiene vista diseño. XD


El Game Maker tiene vista diseño y muchos eventos predefinidos que ayudan bastante, pero sin saber crear scripts no se puede llegar a nada realmente interesante...
Alber_h escribió:
ANTONIOND escribió:
lamateporunyogur escribió:EDIT: Dices que el método bueno es por código, pero: ¿Tiene vista diseño no?

El Game Maker tiene vista diseño. XD


El Game Maker tiene vista diseño y muchos eventos predefinidos que ayudan bastante, pero sin saber crear scripts no se puede llegar a nada realmente interesante...


Pues yo no tengo ni idea de nada...¿Tutos de scripts?

EDIT: Con lo de si tiene vista diseño o no, me referia al Palib.
EDIT#2: Gracias por contestar!
lamateporunyogur escribió:EDIT: Con lo de si tiene vista diseño o no, me referia al Palib.


El PAlib no tiene ninguna vista de diseño, almenos la ultima vez que la usé :s (no creo que lo hayan puesto por eso).

pd: no te rebajes tanto, no uses game maker :p eso es para empezar no teniendo ni idea de programar
13 respuestas