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.

enter image description here

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

Popular posts from this blog

php - Invalid Cofiguration - yii\base\InvalidConfigException - Yii2 -

How to show in django cms breadcrumbs full path? -

ruby on rails - npm error: tunneling socket could not be established, cause=connect ETIMEDOUT -