【问题标题】:Flawed collision detection between rectangles矩形之间有缺陷的碰撞检测
【发布时间】:2015-10-10 17:24:12
【问题描述】:

我正在使用 Pygame 创建一个基于物理的游戏,其中玩家控制一个球。当您控制球时,它会沿指定方向加速(按住左箭头会在其移动速度上增加每帧 x 个像素)。由于球是……嗯……一个球,而且 Pygame 不支持球碰撞检测,我创建了一个具有自己的碰撞方法的新类。该方法有两个部分:如果球跑到矩形的角上,或者如果它跑到矩形的边上。问题与圆与侧碰撞有关。

球是基于一个矩形对象,因此有那些讨厌的角落。我不能使用简单的colliderect 方法,否则上述情况会检测到不应该存在的碰撞,并且它会与我的碰撞检测方法的第一部分重叠。相反,我选择在矩形和球矩形两边的中点之间使用collidepoint

最后,问题的核心。我之前提到过球会加速。当球加速到某个点(即使它看起来静止不动)时,它会移动到足够远的位置进入矩形,以便圆上的另一个中点检测到“碰撞”。这个问题可能源于以下事实(对于球左侧的碰撞)我的代码将球的left 设置为等于矩形的right,因此当球加速到足以进入矩形时,它被移动到矩形的另一个面。

非常感谢您对我的包容,欢迎任何和所有建议。我要么正在寻找解决我的特定问题的方法,要么寻找一种更简洁的方式来处理碰撞检测。我的完整代码如下:

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):
        for 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

        for key in self.new_dir:
            if self.new_dir[key] and key=='left':
                self.speed[0] *= (-1)*self.b_fact
            if self.new_dir[key] and key=='right':
                self.speed[0] *= (-1)*self.b_fact
            if self.new_dir[key] and key=='up':
                self.speed[1] *= (-1)*self.b_fact
            if self.new_dir[key] and 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 = []

        for side in side_list:
            if rect.collidepoint(eval('self.rect.mid'+side)):
                collision_list.append(side)

        for 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)

        for 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 is 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)
        for rect in self.allRects:
            pygame.draw.rect(self.screen, Color['black'], rect)



    def mainloop(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            for i in range(2):
                k = (pygame.KEYUP, pygame.KEYDOWN)
                if event.type == k[i]:
                    if event.key == pygame.K_LEFT:
                        self.ball.move['left'] = i
                    elif event.key == pygame.K_UP:
                        self.ball.move['up'] = i
                    elif event.key == pygame.K_RIGHT:
                        self.ball.move['right'] = i
                    elif event.key == pygame.K_DOWN:
                        self.ball.move['down'] = i

        self.ball.move_self()
        self.ball.bounce(self.allRects)

        self.drawScreen()

        pygame.display.update()
        self.mainClock.tick(20)

Game = BallGame()
while True:
    Game.mainloop()

【问题讨论】:

    标签: python pygame collision-detection


    【解决方案1】:

    考虑碰撞的另一种方法是考虑黑色矩形的放大版本。这将是一个圆角半径为 r 的圆角矩形。小球与黑色矩形的碰撞相当于小球中心与圆角矩形的碰撞。这有助于更轻松地分析情况。

    当它反弹时,确定新位置的更准确方法是考虑从先前位置到当前位置的线。您可以计算出这条线与边界的交叉位置以及完美反射的位置。

    【讨论】:

    • 你有什么想法来处理边缘之间的碰撞吗?我不能使用基本的colliderect,因为正如我前面提到的,它会干扰已经起作用的角对圆碰撞测试。
    猜你喜欢
    • 2017-10-30
    • 1970-01-01
    • 2023-01-26
    • 2014-05-05
    • 2011-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多