python - Flawed collision detection between rectangles -
i creating physics-based game pygame in player controls ball. control ball, accelerates in specified direction (holding left arrow adds x pixels per frame movement speed). since ball is... well... ball, , pygame doesn't support ball collision detection, created new class own collision method. method has 2 parts: if ball runs corner of rectangle, or if runs side of rectangle. problem concerns circle-to-side collision.
the ball based on rect object, , therefore has pesky corners. cannot use simple colliderect
method, otherwise situation above detect collision there should none, , overlap first part of collision detection method. instead, opted use collidepoint
between rectangle , midpoints on each side of ball's rectangle.
finally, heart of issue. mentioned earlier ball accelerates. when ball accelerates point (even though appears standing still) moves far enough rectangle midpoint on circle detect "collision." problem stems fact (for collision on left side of ball) code sets ball's left
equal rectangle's right
, when ball accelerates enough inside rectangle, gets moved face of rectangle.
thank bearing me, , suggestions welcome. either looking fix specific problem, or cleaner way handle collision detection. full code below:
import pygame, sys, math global color color = {} color['white'] = (255,255,255) color['black'] = ( 0, 0, 0) color['red'] = (255, 0, 0) color['green'] = ( 0,255, 0) color['blue'] = ( 0, 0,255) global windowwidth, windowheight windowwidth, windowheight = 500, 500 class ball(): def __init__(self, x, y, r): self.rect = pygame.rect(x, y, r, r) self.radius = r/2 self.speed = [0, 0] self.b_fact = 1 self.move = {'left':false, 'right':false, 'up':false, 'down':false} self.new_dir = {'left':false, 'right':false, 'up':false, 'down':false} def move_self(self): if self.move['left']: self.speed[0] -= 2 if self.move['up']: self.speed[1] -= 2 if self.move['right']: self.speed[0] += 2 if self.move['down']: self.speed[1] += 2 if self.speed[0] < 0: self.speed[0] += 1 if self.speed[1] < 0: self.speed[1] += 1 if self.speed[0] > 0: self.speed[0] -= 1 if self.speed[1] > 0: self.speed[1] -= 1 self.rect.left += self.speed[0] self.rect.top += self.speed[1] def bounce(self, rectlist): rect in rectlist: self.collide_rect(rect) if self.rect.left <= 0: self.rect.left = 0 self.new_dir['right'] = true if self.rect.right >= windowwidth: self.rect.right = windowwidth self.new_dir['left'] = true if self.rect.top <= 0: self.rect.top = 0 self.new_dir['down'] = true if self.rect.bottom >= windowheight: self.rect.bottom = windowheight self.new_dir['up'] = true key in self.new_dir: if self.new_dir[key] , key=='left': self.speed[0] *= (-1)*self.b_fact if self.new_dir[key] , key=='right': self.speed[0] *= (-1)*self.b_fact if self.new_dir[key] , key=='up': self.speed[1] *= (-1)*self.b_fact if self.new_dir[key] , key=='down': self.speed[1] *= (-1)*self.b_fact self.new_dir[key] = false def collide_rect(self, rect): x1, y1, r = self.rect.centerx, self.rect.centery, self.radius foundside = 0 foundcorner = 0 side_list = ['left', 'right', 'bottom', 'top'] corner_list = ['topleft', 'topright', 'bottomleft', 'bottomright'] collision_list = [] side in side_list: if rect.collidepoint(eval('self.rect.mid'+side)): collision_list.append(side) corner in corner_list: x2, y2 = eval('rect.'+corner)[0], eval('rect.'+corner)[1] dist = math.sqrt((x2 - x1)**2 + (y2 - y1)**2) if dist < r: if corner.find('left') > -1: corner = corner.replace('left','right') else: corner = corner.replace('right','left') if corner.find('top') > -1: corner = corner.replace('top','bottom') else: corner = corner.replace('bottom','top') collision_list.append(corner) direction in collision_list: if direction.find('left') > -1: self.rect.left = rect.right self.new_dir['left'] = true if direction.find('top') > -1: self.rect.top = rect.bottom self.new_dir['top'] = true if direction.find('right') > -1: self.rect.right = rect.left self.new_dir['right'] = true if direction.find('bottom') > -1: self.rect.bottom = rect.top self.new_dir['bottom'] = true class ballgame(): def __init__(self): pygame.display.set_caption("ball life") pygame.init() self.ball = ball(0, 0, 30) self.allrects = [] rect = pygame.rect(60,60,50,50) self.allrects.append(rect) self.mainclock = pygame.time.clock() self.screen = pygame.display.set_mode((windowwidth, windowheight)) self.basicfont = pygame.font.sysfont(none, 50) def drawscreen(self): self.screen.fill(color['green']) pygame.draw.ellipse(self.screen, color['white'], self.ball.rect) rect in self.allrects: pygame.draw.rect(self.screen, color['black'], rect) def mainloop(self): event in pygame.event.get(): if event.type == pygame.quit: pygame.quit() sys.exit() in range(2): k = (pygame.keyup, pygame.keydown) if event.type == k[i]: if event.key == pygame.k_left: self.ball.move['left'] = elif event.key == pygame.k_up: self.ball.move['up'] = elif event.key == pygame.k_right: self.ball.move['right'] = elif event.key == pygame.k_down: self.ball.move['down'] = self.ball.move_self() self.ball.bounce(self.allrects) self.drawscreen() pygame.display.update() self.mainclock.tick(20) game = ballgame() while true: game.mainloop()
another way think collision consider enlarged version of black rectangle. rounded rectangle corner radius r. collision between ball , black rectangle equivalent collision between center of ball , rounded rectangle. can make analysis of situation easier.
when bounces more accurate way of determining new position consider line previous position current position. can calculate line crosses boundary , prefect reflection should be.
Comments
Post a Comment