[C con Clase] Necesito Ayuda

Steven Davidson steven en conclase.net
Mar Feb 13 21:51:32 CET 2007


Hola Abraham,

El pasado 2007-02-11 23:13:06, Abraham escribió:

A> Hola 
A> q tal, stoy apenas introduciendome a graficon con C.. y me dejaron hacer un programa q una bolita rebote en la orilla de la pantalla. solo eh logrado q salga de un extremo de la pantalla pero no q rebote, como podre hacer q rebote sta pelota? (aun no lo eh terminado, por eso aun termino esas funciones)

Tu problema implica simular algunas reglas físicas. En particular, tendremos que simular el rebote según el ángulo de incidencia. Esto es, se trata del mismo ángulo creado por el vector de movimiento del objeto con la superficie colisionada que por el vector del objeto en movimiento al ser rebotado o reflejado por la superficie. Por ejemplo, si el objeto choca contra un lado de la "pantalla" a un ángulo de 30°, entonces rebotará a un ángulo de 30°.

Esto implica que necesitamos aplicar un algoritmo de colisión tanto para detectarlas como para resolverlas.

Sugiero crear estructuras para describir el objeto tanto gráfica como matemática o físicamente. Por ejemplo,

typedef struct coord  { int x,y; } punto, vector;

struct circulo_t
{
  punto centro;
  int radio;
  vector vel;
};

Creo que esto representa adecuadamente un círculo, o al menos contiene la información necesaria para aplicar nuestros algoritmos.

Ahora tenemos que aplicar las reglas matemáticas y físicas para que la simulación refleje un comportamiento realista de los objetos. Para esto, se suele seguir el siguiente algoritmo:

1. Detectar si hay colisiones.
2. Responder debidamente a las colisiones existentes.

Aplicamos este algoritmo a la hora de calcular la nueva posición del objeto. Obviamente, no queremos actualizar su posición entre cada fotograma sin determinar si esa nueva posición es válida.

Como se trata de círculos, podemos averiguar los puntos en su perímetro rápidamente. De hecho, usamos el radio con relación a su centro para ello. Calculamos la distancia del nuevo centro del círculo al borde de la pantalla, que es una línea recta - técnicamente, es un segmento, pero esto no es terriblemente importante. Como los bordes de la pantalla son líneas verticales y horizontales, podemos optimizar el cálculo comprobando si los puntos horizontales y verticales en la circunferencia sobrepasan los bordes de la pantalla. Por ejemplo,

struct circulo_t circulo;
...
struct circulo_t nuevo = circulo;

nuevo.centro.x += vel.x;  nuevo.centro.y += vel.y;

// Comprobamos si 'nuevo' choca contra el borde derecho => xmax
if( nuevo.centro.x + nuevo.radio > xmax )
{
  // ¡Colisión!  =>  Simular física => Modificar 'nuevo'
  ...
}
else {...}

Hay que seguir con las comprobaciones de los demás "bordes".

Para simular el rebote, tendrás que averiguar el ángulo con el que el círculo choca contra el borde. Esto es mejor usando vectores:

u . v = ||u|| * ||v|| * cos a,  donde 'u' y 'v' son vectores y 'a' es el ángulo

Por lo tanto, el ángulo es,

a = acos ( u . v / ( ||u|| * ||v|| ) )

Sin embargo, en este caso, ya sabemos que el "otro" vector siempre será un borde horizontal o vertical, podemos determinar el vector de incidencia cambiando signos y lugares de sus valores.


Te pongo un ejemplo,

circulo.centro := (10,40)
circulo.radio  := 5
circulo.vel    := (1,1)

nuevo.centro = circulo.centro + circulo.vel
             = (10,40) + (1,1) = (11,41)

Comprobación:  xmax = 319

11 + 5 > 319 ?  No. No hay colisión.
11 - 5 < 0   ?  No. No hay colisión.

Comprobación:  ymax = 199

41 + 5 > 199 ?  No. No hay colisión.
41 - 5 < 0   ?  No. No hay colisión.

Como no hay colisiones, entonces podemos continuar con actualizar la nueva posición del círculo y dibujarlo para el nuevo fotograma.


Otro ejemplo,

circulo.centro := (312,29)
circulo.radio  := 5
circulo.vel    := (3,2)

nuevo.centro = circulo.centro + circulo.vel
             = (312,29) + (3,2) = (315,31)

Comprobación:  xmax = 319

315 + 5 > 320 ?  Sí. Hay colisión.
315 - 5 < 0   ?  No. No hay colisión.

Comprobación:  ymax = 199

31 + 5 > 199 ?  No. No hay colisión.
31 - 5 < 0   ?  No. No hay colisión.

Como existe una colisión, tenemos que corregir la posición. Simularemos el choque calculando la nueva posición según el vector de incidencia. Siguiendo el ejemplo, esto es,

La colisión es con el borde derecho, por lo que el vector de incidencia es:

nuevo.vel.x = -circulo.vel.x;
nuevo.vel.y =  circulo.vel.y;

Por último, calculamos la distancia total recorrida según la magnitud de la velocidad. Como la trayectoria que sigue el círculo es dividida en dos partes (la original y la "rebotada"), debemos tener en cuenta las distancias de las dos rutas. Para averiguar las distancias, calculamos el módulo del vector de velocidad. Esto sería,

||circulo.vel|| = raíz( 3*3 + 2*2 ) = raíz( 13 ) = 3,6056

Pero como chocamos contra el lado derecho de la pantalla, nos movemos una parte hacia la derecha, y luego la parte restante hacia la izquierda. Tenemos que averiguar donde se corta con el borde o mejor dicho donde choca contra ello y así averiguar las distancias.

Lo anterior sería en general, pero en nuestro caso sabemos que las colisiones se producirán con los bordes de la pantalla que son líneas horiztonales y verticales. Por lo tanto, podemos optimizar un poco este proceso sin tener que estar calculando mucho. En nuestro ejemplo, el choque se hace después de mover 2 unidades a la derecha. Esto es porque la circunferencia será 319:

circulo.centro.x + radio = 312 + 5 = 317

Después de 2 unidades, tenemos 317+2 = 319 justo la coordenada del borde derecho. Luego, la coordenada final será 318 = 319-1, ya que el rebote nos fuerza a ir a la izquierda. Para mudarnos en la vertical, agregamos 2 a la altura. Al final, obtenemos que, el nuevo centro del círculo es (313,31) con una velocidad de (-3,2).

nuevo.centro.x = circulo.centro.x + 2 - 1;  // = 3 unidades en la horizontal
nuevo.centro.y = circulo.centro.y + 2;      // = 2 unidades en la vertical

El problema de esta técnica, es que tendremos que volver a aplicarla por si acaso el resultado vuelva a dar otro choque. Por ejemplo, si estamos muy cerca de una esquina, es posible que choquemos con dos lados; uno seguido del otro.


Otro método es seguir y simular la trayectoria avanzando el círculo por cada píxel e ir comprobando si nos chocamos o no. Para hacer esto óptimamente, es mejor usar un algoritmo de trazado basado en números enteros, como son los algoritmos DDA y de Bresenham.


Me temo que salió larga la explicación. Este tema es algo complicado de explicar, porque estamos intentando simular ciertos aspectos de la física, y ésta tiene varias reglas sencillas de entender, pero menos sencillas de programar.


Espero que todo esto te sirva.

Steven


Más información sobre la lista de distribución Cconclase