0

I'm trying to program a simple simulator of collisions between blocks with PyGame. I've tried to watch several videos online on how to detect collisions in a better way and I've managed to write this function:

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

    # storing initial velocities of the two blocks
    block1_initialV = block1.v
    block2_initialV = block2.v

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

        # 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 and block1.v < 0 :

            self.collision_sound.play()

            # calculating new velocities
            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 and block1.v > 0:

            self.collision_sound.play()

            # calculating new velocities                
            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)

the new velocities are calculated using the formula of elastic collisions (the linear momentum and kinetic energy are conserved). Where block1 and block2 are two object of a class (which I've written) and block1/2.block is the PyGame Rect object used to represent the block. Block1/2.v and block1/2.x are block's velocity and position, while block/2.m is the mass of the block. The block has a width = height = f(m) = 20 + 10ln(m). The function above is called inside the main loop of the application in this part:

while self.running:

    ...

    # updating the value of block's coordinates and check for collisions
    for b_ in self.blocks:

        # checking for collisions with other blocks
        for b__ in self.blocks:
            if not b_ == b__:
                self.block_collisions(b_, b__)

                # updating blocks coordinates
                b_.x += round(b_.v, 0)
    ...

where self.blocks is a list containing several Block class onbjects (each of which has the same properties described above). When I try to run the animation, initially it works fine, but at a certain moment the blocks colliding with each other remain attached instead of bouncing. I can tell, because I've added a sound to this animation, that when this bug occurs it is due to the blocks that still are in collision (the sound repeats itself) and can't somehow separe (maybe because the velocity is calculated more than one time and this cause a flutuation between +k and -j values, which results in an overall velocity of k-j for the two blocks). I can't tell what's the issue in here, have you got any ideas?

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Luke__
  • 227
  • 1
  • 9
  • Have you checked that blocks do not remain in collision because of the collision_tolerance? – zwitsch Jun 29 '21 at 22:13
  • Related [How do I detect collision in pygame?](https://stackoverflow.com/questions/29640685/how-do-i-detect-collision-in-pygame/65064907#65064907) – Rabbid76 Oct 17 '21 at 15:05

1 Answers1

0

Your problem is similar to the one described in the question Sometimes the ball doesn't bounce off the paddle in pong game.

When the blocks collide then they overlap and the direction of movement of the blocks is reversed. If the energy and thus the movement is not sufficient to separate the blocks, i.e. to move the blocks so far that they no longer overlap, a collision is detected again in the following frame, but with a reversed direction of movement. Dies führt wieder zu einer Umkehr der Bewegungsrichtung. This is then repeated continuously and leads to the fact that the blocks hang together with trembling back and forth movement.

A possible solution to the problem is to shift the blocks when they collide so that they do not overlap but only touch each other:

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


        if block1.v < 0:
            # calculating new velocities
            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)

            # calculate overlap
            overlap = block2.block.right - block1.block.left

            # shift blocks
            block1.block.left += overlap // 2
            block2.block.right = block1.block.left
            
        else:
            # calculating new velocities                
            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)

            # calculate overlap
            overlap = block1.block.right - block2.block.left

            # shift blocks
            block2.block.left += overlap // 2
            block1.block.right = block2.block.left
Rabbid76
  • 202,892
  • 27
  • 131
  • 174