【问题标题】:How do I create collision detections for my bouncing balls?如何为我的弹跳球创建碰撞检测?
【发布时间】:2010-10-21 06:41:10
【问题描述】:

我为三个沙滩球在屏幕上弹跳编写了一个动画(在 python 中)。我现在希望它们全部碰撞并能够相互反弹。如果能提供任何帮助,我将不胜感激。

import pygame
import random
import sys
class Ball:


    def __init__(self,X,Y):

        self.velocity = [1,1]
        self.ball_image = pygame.image.load ('Beachball.jpg'). convert()
        self.ball_boundary = self.ball_image.get_rect (center=(X,Y))
        self.sound = pygame.mixer.Sound ('Thump.wav')
        self.rect = self.ball_image.get_rect (center=(X,Y))

if __name__ =='__main__':

    width = 800
    height = 600
    background_colour = 0,0,0
    pygame.init()
    window = pygame.display.set_mode((width, height))
    pygame.display.set_caption("Bouncing Ball animation")
    num_balls = 3
    ball_list = []
    for number in range(num_balls):
        ball_list.append( Ball(random.randint(10, (width - 10)),random.randint(10, (height - 10))) )
    while True:
        for event in pygame.event.get():
                print event 
                if event.type == pygame.QUIT:
                        sys.exit(0)
        window.fill (background_colour)

        for ball in ball_list:
                if ball.ball_boundary.left < 0 or ball.ball_boundary.right > width:
                        ball.sound.play()
                        ball.velocity[0] = -1 * ball.velocity[0]
                if ball.ball_boundary.top < 0 or ball.ball_boundary.bottom > height:
                        ball.sound.play()
                        ball.velocity[1] = -1 * ball.velocity[1]

                ball.ball_boundary = ball.ball_boundary.move (ball.velocity)
                window.blit (ball.ball_image, ball.ball_boundary)
        pygame.display.flip()

【问题讨论】:

标签: python collision-detection


【解决方案1】:

任意形状的碰撞检测通常非常棘手,因为您必须确定是否有任何像素发生碰撞。

使用圆圈实际上更容易。如果您有两个半径分别为 r1 和 r2 的圆,如果中心之间的距离小于 r1+r2,则发生碰撞。

两个中心(x1,y1)和(x2,y2)之间的距离可以计算比较为:

d = sqrt((y2-y1) * (y2-y1) + (x2-x1) * (x2-x1));
if (d < r1 + r2) { ... bang ... }

或者,正如 jfclavette 所指出的,平方根很昂贵,因此最好只使用简单的操作来计算:

dsqrd = (y2-y1) * (y2-y1) + (x2-x1) * (x2-x1);
if (dsqrd < (r1+r2)*(r1+r2)) { ... bang ... }

棘手的一点在于计算新的运动矢量(给定对象的 (x,y) 随时间变化的速率),因为您需要考虑当前的运动矢量和接触点。

我认为作为第一次切割,您应该只反转运动矢量以测试碰撞检测是否首先起作用。

然后再问一个问题 - 最好将个别问题具体化,以便有针对性地回答。

【讨论】:

  • 请注意:平方根是相当昂贵的运算,您可以对等式两边取平方,因为它们都是正数。这给了你 d^2 = (y2-y1) * (y2-y1) + (x2-x1) * (x2-x1) 和 (d^2
  • 好点,@jfclavette,特别是如果你想要最大帧/秒,并入答案。
【解决方案2】:

检测碰撞只是第一步。让我们分解一下。

最快的方法是计算它们的方形边界框,看看它们是否发生碰撞。两个边需要交叉(1 的顶部和底部或 2,1 的左侧和 2 的右侧,反之亦然)以使边界框重叠。没有重叠,没有碰撞。

现在,当它们确实重叠时,您需要计算它们之间的距离。如果这个距离大于球的半径之和,则没有碰撞。

好的!我们有两个球相撞。 现在什么?好吧,他们必须互相反弹。它们反弹的方式取决于几个因素。

首先是它们的弹性。两个相互反弹的橡胶球与两个玻璃球的反弹方式不同。

第二个是它们的初始速度。 Inertia 表示,他们将希望继续朝着最初的方向前进。

第三个是球的质量。质量较小的球会以更高的速度从质量大得多的球上反弹。

让我们先处理第二个和第三个因素,因为它们是相互交织的。

两个球很少会完全命中。扫视的可能性要大得多。在任何情况下,冲击都会沿着球碰撞的切线法线发生。给定它们的初始速度,您需要计算两者沿此法线的矢量分量。这将导致两个球都会带来一对正常速度的碰撞。将总和相加并将其存储在方便的地方。

现在我们必须弄清楚每个球会带走什么。每个球的最终法向速度与给定球的质量成反比。也就是说,取每个球质量的倒数,将两个质量加在一起,然后根据球的质量与两个球质量的倒数之和的比值,将合成的法向速度从碰撞中分离出来。然后加上切向速度,得到球的合成速度。

弹性几乎是相同的,只是它需要一些基本的微积分,因为即使球在压缩时仍然在移动。我会留给你找到相关的数学。

【讨论】:

  • 圆/圆碰撞比轴对齐边界框到轴对齐边界框更容易。跳过边界框部分:)
  • @jfclavette:我认为他在计算距离之前使用边界框作为更容易计算的第一个潜在碰撞检查。
  • @gnovice:是的,我明白这一点。但是 Sphere/Sphere 或 Circle/Circle 比 Box/Box 快。第一次检查实际上比最终检查更难计算。因此我们应该跳过它。
  • @jfclavette:我很想知道为什么你认为距离计算比“abs(x1 - x2) = abs(y1 - y2)”更快。
  • 嗯,我在考虑整数,你基本上是在用 2 个 abs 换 3 个 muls。对于浮点,abs 很便宜,所以你说得对,它实际上可能更快。仍然认为对于已经通过高效碰撞测试的东西来说这不值得。
【解决方案3】:

Pax's answer 很好地涵盖了检测碰撞。关于让对象相互反弹,我建议查看以下有关elastic collisionsinelastic collisionscoefficients of restitution 的链接。

编辑:我刚刚注意到another SO question 中涵盖了这一点,尽管不是专门针对 Python。您还应该在那里查看一些好的链接。

【讨论】:

    【解决方案4】:

    我认为你们缺少一些更简单的东西,尤其是考虑到他正在使用 pygame。

    调用get_rect函数可以为图像设置大概的边界,创建的Rect用于计算图像的位置,如果动画中有多个对象,则可以用于检测碰撞。

    colliderect & rect 可以使用,问题是我不知道你将如何实现它,特别是对于未知数量的球。

    记住它是 python。

    【讨论】:

      【解决方案5】:

      在 CPU 周期非常重要的美好旧时代,编码人员使用一个简单的技巧来检测碰撞:他们使用的颜色可以从像素颜色中分辨出它是背景还是物体。至少在一些 C64 游戏中是这样做的。

      不过不知道你是否愿意走这条路..

      【讨论】:

        【解决方案6】:

        首先你需要检查与rect.colliderect(other_rect)的冲突

        之后,如果它们发生碰撞,您可以检查像素完美碰撞。所以你不会弄乱对象的半径或形状。

        对于像素完美碰撞检查,我使用 Masks: 用mask.from_surface 制作两个蒙版对象,然后将它们放到Mask.overlap 函数中。

        【讨论】:

          【解决方案7】:

          我做了一个python碰撞检测if语句,这里是:

          if beach ball 1 x < beach ball 2 x + beach ball 1 width and beach ball 1 x + beach ball 2 width > beach ball 2 x and beach ball 1 y < beach ball 2 y + beach ball 1 height and beach ball 2 height + beach ball 1 y > beach ball 2 y:
              #put needed code here
          

          在您的情况下,使用 3 个弹跳球,您必须为每个球制作 2 个这种格式的 if 语句,以确保碰撞检测完美无缺。我希望这会有所帮助。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-01-28
            • 2016-09-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多