2011-01-06 25 views
7

Estoy haciendo un juego en el que las bolas rebotan alrededor del dentro de de un círculo mucho más grande. El círculo más grande no se mueve.Python y Pygame: colisión de bolas con el interior del círculo

Aquí está el código que estoy utilizando actualmente para estas colisiones:

def collideCircle(circle, ball): 
    """Check for collision between a ball and a circle""" 
    dx = circle.x - ball.x 
    dy = circle.y - ball.y 

    distance = math.hypot(dx, dy) 

    if distance >= circle.size + ball.size: 
     # We don't need to change anything about the circle, just the ball 
     tangent = math.atan2(dy, dx) 
     ball.angle = 2 * tangent - ball.angle 
     ball.speed *= elasticity + 0.251 

     angle = 0.5 * math.pi + tangent 
     ball.x -= math.sin(angle) 
     ball.y += math.cos(angle) 

Se basa en el maravilloso tutorial por Peter Collingridge over here.

Los objetos de círculo y bola son ambas clases, con (x, y), radio, ángulo y velocidad.

Estoy teniendo dos problemas con este método, sin embargo:

  1. el balón salta de (lo que sospecho) es su "punto de anclaje", que parece estar en la esquina superior derecha del círculo.
  2. Al colisionar con el 5% inferior del círculo, no rebota lo suficiente y, por lo tanto, se "hunde" fuera de la pantalla. Supongo que esto se debe a que el rebote no es lo suficientemente alto como para mover la pelota sobre su "punto de anclaje" (incorrectamente colocado).

Después de haber analizado las posibles soluciones, "Detección rápida de colisiones de círculo" [Enlace eliminado debido al límite de correo no deseado], que aunque en Java utiliza el mismo método, todas se relacionan con colisiones externas, mientras que miro rebotar una pelota en el interior de un círculo.

Aquí es también las definiciones de clase de la bola() y el círculo():

class Ball(): 
    def __init__(self, (x,y), size): 
     """Setting up the new instance""" 
     self.x = x 
     self.y = y 
     self.size = size 
     self.colour = (0,128,255) 
     self.thickness = 0 
     self.speed = 0.01 
     self.angle = math.pi/2 

    def display(self): 
     """Draw the ball""" 
     pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness) 

    def move(self): 
     """Move the ball according to angle and speed""" 
     self.x += math.sin(self.angle) * self.speed 
     self.y -= math.cos(self.angle) * self.speed 
     (self.angle, self.speed) = addVectors((self.angle, self.speed), gravity) 
     self.speed *= drag 

class Circle(): 
    def __init__(self, (x,y), size): 
     """Set up the new instance of the Circle class""" 
     self.x = x 
     self.y = y 
     self.size = size 
     self.colour = (236, 236, 236) 
     self.thickness = 0 
     self.angle = 0 # Needed for collision... 
     self.speed = 0 # detection against balls 

    def display(self): 
     """Draw the circle""" 
     pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness 

Gracias de antemano, Nathan

Respuesta

12

Me alegra que le haya gustado mi tutorial. Me gusta tu variación, en realidad debería ser más simple.

En primer lugar, creo que es necesario cambiar la prueba de choque a:

if distance >= circle.size - ball.size: 

Porque cuanto mayor sea el tamaño de la bola, cuanto menor sea la distancia entre el centro y el centro del círculo puede ser. Esto debería hacer que las pelotas reboten en el lugar correcto (dentro del círculo).

Entonces creo que solo tiene que cambiar los letreros para la xey, y todo debería funcionar.

ball.x += math.sin(angle) 
ball.y -= math.cos(angle) 

para mover el balón por la distancia correcta se puede calcular el solapamiento:

overlap = math.hypot(dx, dy) - (circle.size - ball.size) 

if overlap >= 0: 
    tangent = math.atan2(dy, dx) 
    ball.angle = 2 * tangent - ball.angle 
    ball.speed *= elasticity 

    angle = 0.5 * math.pi + tangent 
    ball.x += math.sin(angle)*overlap 
    ball.y -= math.cos(angle)*overlap 

Buena suerte

+0

¿El eje X no estaría definido por el coseno del ángulo y el eje Y por el seno? ¿O aprendí mal? : X – JCM

+0

Creo que tienes razón en que esa es la manera estándar de definir las cosas, pero mientras seas consecuente en realidad no hace ninguna diferencia. Claramente, si gira todo 90 grados, eso no debería afectar la forma en que las cosas rebotan (suponiendo que la gravedad también cambie). –

+0

@PeterCollingridge, ¡sus artículos son * increíbles *!Soluciones elegantes, estilo python, felicidades ... ¡y gracias! – MestreLion

2

La mayoría de los paquetes de gráficos usan superior izquierda, como comienzo de la elaboración de códigos. Lo más probable es que desee 2 conjuntos de coordenadas, la que colisiona/mueve/etc. con y la de dibujo (x-radius, y-radius).

Además, sin haberlo pensado demasiado, ¿el cheque para la intersección debe ser distance + ball.size >= circle.size? La distancia de las bolas desde el centro más su radio debe ser menor que el radio del círculo, si entendí la configuración correctamente.

12

Sin responder a su pregunta, me gustaría comentar su estrategia de implementación y recomendar un nuevo enfoque. Usted representa la velocidad de la bola en forma de coordenadas polares, como ball.angle y ball.speed.

Creo que esto generalmente será inconveniente para usted. Por ejemplo, en el código de colisión terminas llamando al atan2 para convertir el vector (dx, dy) en un ángulo, y luego llamas al sin y cos para volver a convertir el ángulo en un vector. (Además, si alguna vez intentas generalizar tu código a tres dimensiones, te encontrarás en un mundo de dolor). Por lo tanto, a menos que tengas requisitos particulares que requieran coordenadas polares, te recomiendo que hagas lo que hacen todos los demás, es decir, representar la velocidad de la bola en coordenadas cartesianas como el vector (vx, vy).

También recomiendo cambiar su enfoque de física de un estático uno ("¿es el objeto A actualmente colisionando con el objeto B?") a un dinámico uno (" objetará A colisionar A con el objeto B durante su siguiente paso de movimiento? "). En un sistema de física estática a menudo termina con objetos que se intersectan al final de un paso de movimiento, y luego usted tiene que averiguar la mejor manera de conseguir que se separan de nuevo, que es difícil de hacerlo bien.

Si lo hace ambas cosas, es sencillo para hacer rebotar la pelota sin ninguna trigonometría.

Paso 1 Transforme la colisión círculo/círculo en colisión de punto/círculo usando Minkowski addition:

Original problem at left shows small circle moving inside a large circle. Transformed problem at right shows point moving in circle whose radius is the difference between the radii of the circles in the original problem.

Paso 2. Considere un segmento de tiempo en el que la bola comienza a p = (px, py) y se mueve por v = (vx, vy). ¿Se cruza con el círculo? Puede usar un standard line segment/circle test para esto, excepto que el sentido de la prueba se invierte.

Paso 3. Encuentre el punto de colisión c = (cx, cy). La pelota rebota en el círculo de la misma manera que rebotaría en la línea t tangente al círculo en este punto. Para un círculo centrado en el origen, el vector tangente es justo (-cy, cx) y estoy seguro de que puedes calcular cómo calcularlo para otros círculos.

Figure described above

See this answer de cómo calcular la nueva trayectoria de la pelota sobre la base de coeficientes de fricción y la restitución.

Paso 4. No olvide que la pelota todavía puede tener cierta distancia para moverse a lo largo del nuevo vector w. Si el paso de tiempo es lo suficientemente grande o la velocidad es lo suficientemente alta, puede colisionar nuevamente durante el mismo segmento de tiempo.

+0

Aunque esto no era lo que estaba buscando, gracias montones! Estoy investigando. – nchpmn

Cuestiones relacionadas