首页 > 解决方案 > 方块之间的碰撞

问题描述

我正在尝试编写一个简单的弹性碰撞模拟器(在单个维度上,沿 x 轴移动直线运动)。经过几次测试,我设法编写了这个函数来检查碰撞:

def block_collisions(self, block1, block2):
        """ check whether the two passed block objects happen to collide """

        # calculating a collision tollerance threshold under which the potential collision is considered to happen
        collision_tolerance = 2*round(abs(block1.v) + abs(block2.v), 0) + 5

        # storing initial velocities of the two blocks (since the new velocities od the blocks are to be
        # calculated independently and at the same time)
        block1_initialV = block1.v
        block2_initialV = block2.v

        if block1.block.colliderect(block2.block):


            if ([block1, block2] not in self.collisions) and ([block2, block1] not in self.collisions):
                self.collisions.append([block1, block2])

                # the collision can happn only on the left and right side of the blocks since it is a rectilinear motion
                if abs(block2.block.right - block1.block.left) < collision_tolerance:

                    self.collision_sound.play()

                    # calculating new velocities
                    if self.is_elastic:
                        block1.v = (2*block2.m*block2_initialV + (block1.m - block2.m)*block1_initialV) / (block1.m + block2.m)
                        block2.v = (2*block1.m*block1_initialV + (block2.m - block1.m)*block2_initialV) / (block1.m + block2.m)
                    
                if abs(block2.block.left - block1.block.right) < collision_tolerance:

                    self.collision_sound.play()

                    # calculating new velocities
                    if self.is_elastic:                
                        block1.v = (2*block2.m*block2_initialV + (block1.m - block2.m)*block1_initialV) / (block1.m + block2.m)
                        block2.v = (2*block1.m*block1_initialV + (block2.m - block1.m)*block2_initialV) / (block1.m + block2.m)

                if block1.x <= block2.x:
                    block1.x = block2.x
                    
                if block1.x <= block2.x:
                    block1.x = block2.x

该函数将两个 Block 类(我已经编写过)对象作为参数。它的主要属性是块当前和初始位置:block.x、block.x0;当前和初始速度:block.v,block.v0;以及用于在屏幕上表示实际矩形的 Pygame rect 对象:block.block。变量 self.collisions 是一个列表,其中包含在主循环的单次迭代期间检测到的所有碰撞对(因此每个循环都会清空)。为确保仅检测到一次碰撞,检查对 [block1, block2] 或 [block2, block1] 是否已经在此列表中。另一种碰撞检测功能是检测墙壁碰撞的功能:

def wall_collisions(self, block):
        """ check whether the a block happens to collide with a particular wall """

        # the wall is assumed to have an ideal infinite mass, hence, the new velocity of the wall is 0, while the velocity of
        # the block is the same, opposite in direction
        if self.l_wall:
            if block.block.left <= self.l_wall:
                self.collision_sound.play()
                block.v *= -1
                
        if self.r_wall:
            if block.block.right >= self.r_wall:
                self.collision_sound.play()
                block.v *= -1

其中 self.l_wall, self.r_wall 是设置墙位置的两个值,例如 self.l_wall = 20, self.r_wall = window_width - 20。这两个函数都放置在一个主循环中,在一个具有以下语法:

while self.__running:

    ...

    for b_ in self.blocks:

        # check block collisions
        for b__ in self.blocks:
            if not b_ == b__:
                block_collisions(b_, b__)

        # check wall collisions
        wall_collisions(b_)
    self.collisions = []

这些函数的问题是,即使结果适用于大多数情况,它也有点小故障和错误:

  1. 让屏幕上有两个块,一个的质量比另一个大一个数量级。当较小的方块恰好在一面墙和另一个移动到同一面墙上的方块之间时,由于大量的反弹,较小的方块似乎闪烁并与墙壁和方块重叠(物理结果仍然可以接受,但整体表示似乎不准确)。

  2. 让较大的块比另一个块大一个数量级以上。在上述情况下,较大的块没有及时停止,而是将较小的块挤在墙上(墙与较大块之间的距离变得小于较小块的边)。然而即使在这种情况下,物理结果也是可以接受的,因为问题是图形而不是方法论的。

  3. 让速度很高 (>= 20m/s = 20 * (50px / 60frames) = 16.7 px/frame, 50px = 1m, 60frames = 1s) 并且质量之间的差异很大 (m1 / m2 >= 100 iff m1 > m2 ),考虑到一个指向另一个的块,它们倾向于重叠,因此较小的块以某种方式设法穿透较大的块。在这个尴尬的事件之后,碰撞继续被检测到,因此较小的块被困在另一个块中。

有谁了解我遇到的这些问题并知道如何解决?或者有没有人知道更好、更精确的方法来检查碰撞?

标签: pythonpygamecollision-detectioncollision

解决方案


推荐阅读