Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Colisiones Con Pygame: Pregunta De Novato 100%

Iniciado por Pepius, 01 de Agosto de 2005, 07:54:58 PM

« anterior - próximo »

Pepius

 Es una pregunta que no esta al nivel del foro, ni mucho menos. De hecho, me siento un poco ridiculo posteandolo, pero espero que me ayudeis.

Utilizo pygame, pero os voy a explicar el problema de forma que entendais aunque no sepais python ni pygame.

Estoy haciendo un simple arcanoid. Estoy en el tema de los rebotes etc. Cuando la bola golpea el bloque necesito identificar si ha golpeado uno de los laterales o si a golpeado la parte de arriba o abajo, para que la bola rebote en funcion. Y es ahi donde tengo el problema. Yo hago esto:

-Inicio bucle que pasa por todos los bloques para ver si colisionan con la bola.
      - Si colisionan empiezo otro bucle que recorre toda la coordenada Y del bloque, desde arriba hasta abajo.
               -Dentro del bucle chequeo si colisionan(la pelota y el bloque) en el punto [X1,Y1], cuando X1 es el punto         más a la derecha de la bola, y Y1 es el la variable que recorre el bucle de la coordenada Y. Despues lo vuelvo a hacer pero cambiando X1 no es el punto más a la derecha, si no a la izquierda.
                         - Si colisionan la bola hace el rebote correspondiente, elimina el bloque y hace un break en el      bucle.

      -Ahora vuelvo a hacer un bucle pero recorre la coordenada X del bloque, de izquierda a derecha.
               - Vuelvo a ver si colisionan en el punto [X2,Y2], pero ahora X2 es la variable del bucle y Y2 es la parte más alta primero y despues la más baja.
                           - Si colisionan hago el rebote correspondiente, etc...


La verdad, no se si esta bien o es el metodo más estupido que existe. El problema reside en que, aunque testeando yo veo que la pelota le da en la parte alta/baja, rebota como si colisionara con los lados. Se que el problema esta en que, como en el codigo el chequeo de si colisiona en los lados esta primero, coge este. Ya que lo he cambiado de orden i se altera el efecto.


En fin, que me he explicado como una mierda, pero espero que me ayudeis. Si quereis pongo el trozo de codigo.

Saludos, y perdon por la novatada.

Zaelsius

 No te preocupes... yo de cabeza tampoco soy capaz de escribir el código adecuado a la primera sin dejarme ningun caso. Es más, he visto fallar algunos ejemplos de "arkanoid" de Sun para J2ME... Aquí te pongo un fragmento de código cogido de un artículo de CodeProject que he encontrado con Google:

http://www.codeproject.com/directx/SuperBr...rickBreaker.asp (registro gratuito requerido para descarga de los fuentes, aunque no comprueba validez del e-mail)

///////////////////////////////////////////////////////////////////////////////
/// Receives: RECT describing current position of ball
///           RECT describing brick or paddle being tested for interesection
/// Returns:  EIntersect describing how ball rect intersected rectangle
///////////////////////////////////////////////////////////////////////////////
EIntersect CSuperBrickBreaker::BallRectIntersect(RECT p_ball_rect, RECT p_rect)
{
bool l_contains_top_left  = false;
bool l_contains_top_right = false;
bool l_contains_bot_left  = false;
bool l_contains_bot_right = false;

// check if top-left corner of the ball is in the rectangle
l_contains_top_left = ( (p_ball_rect.top  >= p_rect.top)  && (p_ball_rect.top  <= p_rect.bottom) &&
                     (p_ball_rect.left >= p_rect.left) && (p_ball_rect.left <= p_rect.right) );

// check if top-right corner of the ball is in the rectangle
l_contains_top_right = ( (p_ball_rect.top   >= p_rect.top)  && (p_ball_rect.top   <= p_rect.bottom) &&
                      (p_ball_rect.right >= p_rect.left) && (p_ball_rect.right <= p_rect.right) );

// check if bottom-left corner of the ball is in the rectangle
l_contains_bot_left = ( (p_ball_rect.bottom >= p_rect.top)  && (p_ball_rect.bottom <= p_rect.bottom) &&
                     (p_ball_rect.left   >= p_rect.left) && (p_ball_rect.left   <= p_rect.right) );

// check if bottom-right corner of the ball is in the rectangle
l_contains_bot_right = ( (p_ball_rect.bottom >= p_rect.top)  && (p_ball_rect.bottom <= p_rect.bottom) &&
                      (p_ball_rect.right  >= p_rect.left) && (p_ball_rect.right  <= p_rect.right) );

if ( (l_contains_top_left) && (l_contains_top_right) )
{
 return(IS_BOTTOM);
}
else if ( (l_contains_top_right) && (l_contains_bot_right) )
{
 return(IS_LEFT);
}
else if ( (l_contains_bot_right) && (l_contains_bot_left) )
{
 return(IS_TOP);
}
else if ( (l_contains_bot_left) && (l_contains_top_left) )
{
 return(IS_RIGHT);
}
else if (l_contains_top_left)
{
 // only the top left corner 'penetrated' the rectangle
 // if the top-left corner is farther from the bottom of the
 // rectangle than the right side, assume the ball interected
 // the right
 if  ( (p_rect.bottom - p_ball_rect.top) >= (p_rect.right - p_ball_rect.left) )
 {
  return(IS_RIGHT);
 }
 else
 {
  return(IS_BOTTOM);
 }
}
else if (l_contains_top_right)
{
       // only the top right corner 'penetrated' the rectangle
 // if the top-right corner is farther from the bottom of the
 // rectangle than the left side, assume the ball interected
 // the left
 if  ( (p_rect.bottom - p_ball_rect.top) >= (p_ball_rect.right - p_rect.left) )
 {
  return(IS_LEFT);
 }
 else
 {
  return(IS_BOTTOM);
 }
}
else if (l_contains_bot_right)
{
       // only the bottom right corner 'penetrated' the rectangle
 // if the bottom-right corner is farther from the top of the
 // rectangle than the left side, assume the ball interected
 // the left
 if  ( (p_ball_rect.bottom - p_rect.top) >= (p_ball_rect.right - p_rect.left) )
 {
  return(IS_LEFT);
 }
 else
 {
  return(IS_BOTTOM);
 }
}
else if (l_contains_bot_left)
{
 // only the bottom left corner 'penetrated' the rectangle
 // if the bottom-left corner is farther from the top of the
 // rectangle than the right side, assume the ball interected
 // the right
 if  ( (p_ball_rect.bottom - p_rect.top) >= (p_rect.right - p_ball_rect.left) )
 {
  return(IS_RIGHT);
 }
 else
 {
  return(IS_BOTTOM);
 }
}

return(IS_NONE);
}



Respecto a la detección de colisión entre bloques y pelota, si usas una estructura en rejilla no deberías comprobar todos los bloques, sino sólo aquellos que estén alrededor o debajo de la pelota, hablando en términos de celdas.

Sacrifai

 Hazte una función para obtener el angulo que hay entre en centro de la bola y el centro del bloque. Después miras si choca y si el angulo está entre tal valor y tal valor es que ha chocado por tal lado.

PD: No se si me he explicado bien.  :)  

Pepius

 La verdad, este codigo me ha hecho ver las cosas má claras. Aun asi, no funciona, la colision en los laterales no funciona bien. En fin, pongo mi codigo (python):

for blx in pygame.sprite.groupcollide(ballsprite,blocksprites,0,1):
           if blx.rect.collidepoint(bola.rect.topleft) and blx.rect.collidepoint(bola.rect.topright):
               bola.rebound1()
               
           elif blx.rect.collidepoint(bola.rect.bottomleft) and blx.rect.collidepoint(bola.rect.bottomright):
               bola.rebound1()
               
           elif blx.rect.collidepoint(bola.rect.topleft) and blx.rect.collidepoint(bola.rect.bottomleft):
               bola.rebound2()
               
           elif blx.rect.collidepoint(bola.rect.bottomright) and blx.rect.collidepoint(bola.rect.topright):
               bola.rebound2()
               

           
           elif blx.rect.collidepoint(bola.rect.bottomright):
               
               if (bola.rect.bottom - blx.rect.top) >= (bola.rect.right - blx.rect.left):
                   bola.rebound1()
                   
               else:
                   bola.rebound2()
                   
           elif blx.rect.collidepoint(bola.rect.bottomleft):
             
               if (bola.rect.bottom - blx.rect.top) >= (blx.rect.right - bola.rect.left):
                   bola.rebound1()
                   
               else:
                   bola.rebound2()
                   
           elif blx.rect.collidepoint(bola.rect.topleft):
               if (blx.rect.bottom - bola.rect.top) >= (blx.rect.right - bola.rect.left):
                   bola.rebound1()
                   
               else:
                   bola.rebound2()
                   
               
           elif blx.rect.collidepoint(bola.rect.topright):
               if (blx.rect.bottom - bola.rect.top) >= (bola.rect.right - blx.rect.left):
                   bola.rebound1()
                   
               else:
                   bola.rebound2()


Donde blx es el bloque y la funcion collidepoint devuelve True si el punto indicado se encuentra dentro del sprite.

Sobre lo de la rejilla, tienes razon, de hecho el editor de niveles si funciona en rejilla.

Saludos y a ver si me puedes ayudar, muchas gracias!

Pepius

 Ups perdona Sacrifai, no te habia leido.

Pues la verdad es que es una buena idea, pero las matematicas de 3r de ESO no acompañan U_U

En fin, si esto no va probaré :D

Mars Attacks

 Vendría a ser algo así como

vector1.x=bola.x-bloque.x;
vector1.y=bola.y-bloque.y;
vector2.x=0;
vector2.y=-bloque.y;

coseno_angulo=(vector1.x*vector2.x+vector1.y*vector2.y)/(sqrt(vector1.x*vector1.x+vector1.y*vector1.y)+sqrt(vector2.x*vector2.x+vector2.y*vector2.y));
angulo=acos(coseno_angulo);

Lo he puesto de memoria, pero en google puedes buscar "ángulo vectores" y seguro que te saca la fórmula correcta.
Aunque a mí me parece más pesado el cálculo de raíces cuadradas y arcocosenos que la secuencia de condicionales, no sé.

Pepius

 Uhm, no se porque pero la variable angulo siempre es: 1.57079632679

En fin, saludos.

Mars Attacks

 Quizá te la esté dando en radianes, pasa el resultado a grados (degrees). Y revisa la fórmula, no te fíes de lo que he escrito.






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.