thinkpad20

2D ball bouncing collisions code

Nov 20th, 2012
224
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.13 KB | None | 0 0
  1. import pygame, sys, random, math, time, os
  2. from pygame.locals import *
  3.  
  4. bif = "burst.jpg"
  5. mif = "red-dot.png"
  6. ball1image = "ball.png"
  7. ball2image = "golf-ball.png"
  8. ball3image = "circle.png"
  9. boing = "flop.wav"
  10.  
  11.  
  12. WIDTH = 1025
  13. HEIGHT = 822
  14. SPEED = 1
  15. count = 0
  16. x_reflect_count = 0
  17. y_reflect_count = 0
  18.  
  19. def load_sound(name):
  20.     class NoneSound:
  21.         def play(self): pass
  22.     if not pygame.mixer:
  23.         return NoneSound()
  24.     try:
  25.         sound = pygame.mixer.Sound(name)
  26.     except pygame.error, message:
  27.         print 'Cannot load sound:', name
  28.         raise SystemExit, message
  29.     return sound
  30.  
  31. pygame.init()
  32. pygame.mixer.pre_init(44100, -16, 2, 2048)
  33. pygame.mixer.init()
  34.  
  35. screen = pygame.display.set_mode((WIDTH,HEIGHT),0,32)
  36.  
  37. background = pygame.image.load(bif).convert()
  38. WIDTH = background.get_width()
  39. HEIGHT = background.get_height()
  40.  
  41. cursor = pygame.image.load(mif).convert_alpha()
  42. cursor_radius = cursor.get_width()/2
  43.  
  44. ball1 = pygame.image.load(ball1image).convert_alpha()
  45. ball1_radius = ball1.get_width()/2
  46.  
  47. class Ball(object):
  48.     def __init__(self, image, idnum = None):
  49.         """pos and vel are tuples; image is a path to the image file"""
  50.         self.x = 0 # type float
  51.         self.y = 0
  52.         self.vx = 0
  53.         self.vy = 0
  54.         self.image = pygame.image.load(image).convert_alpha() # type pygame image object
  55.         self.radius = self.image.get_width()/2
  56.         self.x_refcount = 0
  57.         self.y_refcount = 0
  58.         self.count = 0
  59.         self.idnum = idnum
  60.         self.mass = math.pi * 4/3 * self.radius**3        
  61.     def get_position(self):
  62.         """Return's the ball's position vector (center of ball)"""
  63.         return (self.x, self.y)    
  64.     def get_velocity(self):
  65.         """Return's the ball's velocity vector"""
  66.         return (self.vx, self.vy)
  67.     def get_speed(self):
  68.         return math.sqrt(self.vx ** 2 + self.vy ** 2)
  69.     def get_direction(self):
  70.         return math.atan2(self.vy, self.vx)
  71.     def get_radius(self):
  72.         """Returns the ball's radius"""
  73.         return self.radius    
  74.     def get_draw_coords(self):
  75.         """Returns the coordinates that the ball will be drawn at"""
  76.         x_draw = self.x - self.radius
  77.         y_draw = self.y - self.radius
  78.         return (x_draw, y_draw)
  79.     def get_edges(self):
  80.         """Return left, right, top and bottom edges"""
  81.         return [self.x - self.radius, self.x + self.radius, self.y - self.radius, self.y + self.radius]
  82.     def move(self):
  83.         """Updates position inx by moving it delta, delta is a float"""
  84.         self.x = (self.x + self.vx * SPEED) % (WIDTH + 1 + SPEED)
  85.         self.y = (self.y + self.vy * SPEED) % (HEIGHT + 1 + SPEED)        
  86.     def update_vel(self, vel):
  87.         """Set the velocity to arbitrary vel tuple"""
  88.         self.vx = vel[0]
  89.         self.vy = vel[1]
  90.     def update_pos(self, pos):
  91.         """Set the position to arbitrary pos tuple"""
  92.         self.x = pos[0]
  93.         self.y = pos[1]
  94.     def reverse_x(self):
  95.         """Flip x direction"""
  96.         self.vx = -1 * self.vx
  97.     def reverse_y(self):
  98.         """Flip y direction"""
  99.         self.vy = -1 * self.vy
  100.     def draw(self, disp, coords = None):
  101.         """Draw the ball on the screen, disp is a pygame display object, coords is a tuple"""
  102.         if not coords: coords = self.get_draw_coords()
  103.         disp.blit(self.image, coords)
  104.     def update_x_refcount(self, increment = True):
  105.         if increment:
  106.             self.x_refcount += 1
  107.         else:
  108.             self.x_refcount = 0
  109.     def update_y_refcount(self, increment = True):
  110.         if increment:
  111.             self.y_refcount += 1
  112.         else:
  113.             self.y_refcount = 0
  114.     def get_x_refcount(self):
  115.         return self.x_refcount
  116.     def get_y_refcount(self):
  117.         return self.y_refcount
  118.     def get_idnum(self):
  119.         return self.idnum
  120.     def get_mass(self):
  121.         return self.mass
  122.     def get_count(self):
  123.         return self.count
  124.     def update_count(self, increment = True):
  125.         if increment:
  126.             self.count += 1
  127.         else:
  128.             self.count = 0
  129.  
  130. def set_inc(theta = None):
  131.     if not theta:
  132.         theta = 2 * random.random() * math.pi
  133.     x_inc = math.cos(theta)
  134.     y_inc = math.sin(theta)
  135.     return x_inc, y_inc
  136.  
  137.  
  138. def manage_bounce(ball1, ball2):
  139.     ball1_coords = ball1.get_position()
  140.     ball1_vel = ball1.get_velocity()
  141.     ball2_coords = ball2.get_position()
  142.     ball2_vel = ball2.get_velocity()
  143.    
  144.     dx = ball1_coords[0] - ball2_coords[0]
  145.     dy = ball1_coords[1] - ball2_coords[1]
  146.     collision_angle = math.atan2(dy, dx)
  147.     magnitude_1 = ball1.get_speed()
  148.     magnitude_2 = ball2.get_speed()
  149.     direction_1 = ball1.get_direction()
  150.     direction_2 = ball2.get_direction()
  151.    
  152.     new_xspeed_1 = magnitude_1 * math.cos(direction_1 - collision_angle);
  153.     new_yspeed_1 = magnitude_1 * math.sin(direction_1 - collision_angle);
  154.     new_xspeed_2 = magnitude_2 * math.cos(direction_2 - collision_angle);
  155.     new_yspeed_2 = magnitude_2 * math.sin(direction_2 - collision_angle);
  156.    
  157.     final_xspeed_1 = ((ball1.get_mass() - ball2.get_mass()) * new_xspeed_1 + 2 * ball2.get_mass() * new_xspeed_2) / (ball1.get_mass() + ball2.get_mass())
  158.     final_xspeed_2 = (2 * ball.get_mass() * new_xspeed_1 + (ball2.get_mass() - ball1.get_mass()) * new_xspeed_2) / (ball1.get_mass() + ball2.get_mass())
  159.     final_yspeed_1 = new_yspeed_1;
  160.     final_yspeed_2 = new_yspeed_2;
  161.    
  162.     ball1_xspeed = math.cos(collision_angle)*final_xspeed_1 + math.cos(collision_angle + math.pi / 2) * final_yspeed_1
  163.     ball1_yspeed = math.sin(collision_angle)*final_xspeed_1 + math.sin(collision_angle + math.pi / 2) * final_yspeed_1
  164.     ball1.update_vel((ball1_xspeed, ball1_yspeed))
  165.     ball2_xspeed = math.cos(collision_angle)*final_xspeed_2 + math.cos(collision_angle + math.pi / 2) * final_yspeed_2
  166.     ball2_yspeed = math.sin(collision_angle)*final_xspeed_2 + math.sin(collision_angle + math.pi / 2) * final_yspeed_2
  167.     ball2.update_vel((ball2_xspeed, ball2_yspeed))
  168.  
  169.  
  170. ball1 = Ball(ball1image, 1)
  171. ball1.update_pos((WIDTH/4, HEIGHT/2))
  172. ball1.update_vel(set_inc())
  173.  
  174. ball2 = Ball(ball2image, 2)
  175. ball2.update_pos((3*WIDTH/4, HEIGHT/2))
  176. ball2.update_vel(set_inc())
  177.  
  178. ball3 = Ball(ball3image, 3)
  179. ball3.update_pos((WIDTH/2, 3*HEIGHT/4))
  180. ball3.update_vel(set_inc())
  181.  
  182. balls = [ball1, ball2, ball3]
  183.  
  184.  
  185. pillar1 = Ball(mif, 1)
  186. pillar1.update_pos((WIDTH/4, HEIGHT/4))
  187. pillar2 = Ball(mif, 2)
  188. pillar2.update_pos((WIDTH/4, 3*HEIGHT/4))
  189. pillar3 = Ball(mif, 3)
  190. pillar3.update_pos((3*WIDTH/4, HEIGHT/4))
  191. pillar4 = Ball(mif, 4)
  192. pillar4.update_pos((3*WIDTH/4, 3*HEIGHT/4))
  193. pillar5 = Ball(mif, 5)
  194. pillar5.update_pos((WIDTH/2, HEIGHT/2))
  195.  
  196. pillars = [pillar1, pillar2, pillar3, pillar4, pillar5]
  197.  
  198.  
  199. cursor_oop = Ball(mif)
  200.  
  201. ball1_x, ball1_y = WIDTH/2,HEIGHT/2
  202. x_inc, y_inc = set_inc()
  203.  
  204. font = pygame.font.Font(None, 25)
  205. bounce_sound = load_sound(boing)
  206.  
  207. pygame.mixer.init()
  208.  
  209. frame_end = time.clock()
  210.  
  211. while True:
  212.    
  213.     for event in pygame.event.get():
  214.         if event.type == QUIT:
  215.             pygame.quit()
  216.             sys.exit()
  217.         if event.type == KEYDOWN:
  218.             if event.key == K_UP:
  219.                 if SPEED < 0.1: SPEED += 0.01
  220.                 else: SPEED += 0.1
  221.             elif event.key == K_DOWN:
  222.                 if SPEED <= 0.1: SPEED -= 0.01
  223.                 else: SPEED -= 0.1
  224.             else:
  225.                 ball1.update_vel(set_inc())
  226.             print "speed = ", SPEED
  227.  
  228.     screen.blit(background, (0,0))
  229.  
  230.     cursor_oop.update_pos(pygame.mouse.get_pos())
  231.  
  232.     #moving and wall collisions#
  233.     for ball in balls:
  234.         ball.move()
  235.         ball_edges = ball.get_edges()
  236.         if ball_edges[0] <= 0 or ball_edges[1] >= WIDTH:
  237.             if ball.get_x_refcount() > 10:
  238.                 ball.reverse_x()
  239.                 ball.update_x_refcount(False)
  240.             x_reflect_count = 0
  241.         if ball_edges[2] <= 0 or ball_edges[3] >= HEIGHT:
  242.             if ball.get_y_refcount() > 10:
  243.                 ball.reverse_y()
  244.                 ball.update_y_refcount(False)
  245.  
  246.     cursor_oop.draw(screen)
  247.     for ball in balls:
  248.         ball.draw(screen)
  249.     for pillar in pillars:
  250.         pillar.draw(screen)
  251.  
  252.     #ball-ball collisions#
  253.     compared_pairs = {(ball, ball) for ball in balls} # we will keep track of which pairs we have compared here
  254.     for ballA in balls:
  255.         for ballB in balls:
  256.             if (ballA, ballB) not in compared_pairs:
  257.                 ballA_coords = ballA.get_position()
  258.                 ballB_coords = ballB.get_position()
  259.                 dist_x = ballB_coords[0] - ballA_coords[0]
  260.                 dist_y = ballB_coords[1] - ballA_coords[1]
  261.                 dist_abs = math.sqrt(dist_x**2 + dist_y**2)
  262.                 if (dist_abs < ballA.get_radius() + ballB.get_radius()) and ballA.get_count() > 10 and ballB.get_count() > 10:
  263.                     print "collision between ball %d and ball %d!" % (ballA.get_idnum(), ballB.get_idnum())
  264.                     ballA.update_count(False)
  265.                     ballB.update_count(False)
  266.                     manage_bounce(ballA, ballB)
  267.                 compared_pairs.add((ballA.get_idnum(),ballB.get_idnum())) # add pair to set so it won't be compared again
  268.                 compared_pairs.add((ballB.get_idnum(),ballA.get_idnum()))
  269.  
  270.         for pillar in pillars:
  271.             dist_x = pillar.get_position()[0] - ball.get_position()[0]
  272.             dist_y = pillar.get_position()[1] - ball.get_position()[1]
  273.             dist_abs = math.sqrt(dist_x**2 + dist_y**2)
  274.             if (dist_abs < pillar.get_radius() + ball.get_radius()):
  275.                 if True:#ball.get_count() > 10:
  276.                     print "ball %d hit pillar %d!" % (ball.get_idnum(),pillar.get_idnum())
  277.                     ball.update_count(False)
  278.                     ball.update_vel((-dist_x/dist_abs, -dist_y/dist_abs))
  279.                 else: print "count was too low (%d)" % ball.get_count()
  280.  
  281.     #mouse-ball collisions#
  282.     cursor_coords = cursor_oop.get_position()
  283.     for ball in balls:
  284.         ball_coords = ball.get_position()
  285.         dist_x = cursor_coords[0] - ball_coords[0]
  286.         dist_y = cursor_coords[1] - ball_coords[1]
  287.         dist_abs = math.sqrt(dist_x**2 + dist_y**2)
  288.  
  289.         if (dist_abs < cursor_oop.get_radius() + ball.get_radius()) and count > 180:
  290.             print "hit ball %d! it took you %d" % (ball.get_idnum(),count)
  291.             count = 0
  292.             ball.update_vel((-dist_x/dist_abs, -dist_y/dist_abs))
  293.    
  294.     #measuring framerate#
  295.     previous_frame = frame_end
  296.     frame_end = time.clock()
  297.  
  298.     elapsed_time = frame_end-previous_frame
  299.     fps = 1/elapsed_time
  300.  
  301.     #displaying text on screen#
  302.     dist_text = font.render("Dist x: %0.02f Dist y: %0.02f Dist abs: %0.02f" % (dist_x, dist_y, dist_abs), 1, (10,10,10))
  303.     screen.blit(dist_text, (0,0))
  304.     posn_text = font.render("Cursor x: %0.02f Cursor y: %0.02f" % cursor_oop.get_position(), 1, (10,10,10))
  305.     screen.blit(posn_text, (0,15))
  306.     time_text = font.render("Time elapsed: %0.07f FPS: %0.07f" % (elapsed_time, fps), 1, (10,10,10))
  307.     screen.blit(time_text, (0,30))
  308.  
  309.     #bookkeeping#
  310.     count += 1
  311.     for ball in balls:
  312.         ball.update_x_refcount()
  313.         ball.update_y_refcount()
  314.         ball.update_count()
  315.     pygame.display.update()
Advertisement
Add Comment
Please, Sign In to add comment