p.ram
p.ram

Reputation: 109

Animation glitch when simulating the collision of two blocks for the calculation of PI

I am using pygame to create a simulation of collision two block having mass ratios in the powers of 100, to be clear it means that ratio of bigger block to smaller block can be 100**0, 100**1, 100**2 and so on. I have added comments and doc strings where necessary to make logic understandable.

import pygame

# from collisions import *
pygame.init()

s1, s2 = 100, 50  # block sides
x1, y1 = 1000, 250  # bigger block coords
x2, y2 = 500, y1 + s1 - s2  # smaller block coords

power = int(input('enter: '))  # mass ratio
v1 = (-0.5)  # initial velocity of block 1

m1, m2 = 100 ** (power - 1), 1  # mass of blocks
v2 = 0  # initial velocity of block 2

# temp_x1 = 0
red = (255, 0, 0)
blue = (0, 0, 255)


def message_to_print(msg, color):
    font = pygame.font.SysFont(None, 40)
    text = font.render(msg, True, color)
    win.blit(text, [10, 10])


def reverse_vel(vel):
    '''
    reversing velocity of block
    '''
    vel *= -1
    return vel


def exchange_vel(v1, m1, v2, m2):
    '''
    this function is calculating the new velocity of the block after collision,
    based on law of conservation of momentum and kinetic energy
    '''
    v1 = ((m1 - m2) / (m1 + m2)) * v1 + ((2 * m2) / (m1 + m2)) * v2

    return v1  # returning new velocity after collision


win = pygame.display.set_mode((1200, 500))
win.fill((255, 255, 255))
pygame.display.set_caption('simulation')

Collisions = 0  # counting number of collisions
run = True
while run:
    # click_sound = pygame.mixer.Sound("rss/click.wav")
    # pygame.time.delay(10)
    # sound_collide, sound_reverse = True, True

    message_to_print('collision ' + str(Collisions), (0, 0, 0))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    # biger block
    x1 += v1  # changing block coordinates according to velocity
    if x1 > s2:  # this prevents block 1 from moving out of window
        t = x1

    if not x2 + s2 < x1 or x1 + s1 < x2:
        '''
        changing velocity after collision,
        storing them in temp variable for each block,
        then assiging new velocity
        '''
        v2_temp = exchange_vel(v2, m2, v1, m1)
        v1_temp = exchange_vel(v1, m1, v2, m2)
        # if sound_collide:
        # click_sound.play()
        # sound = False

        v2, v1 = v2_temp, v1_temp  # assigning new velocities
        Collisions += 1

    # smaller Block
    x2 += v2
    if x2 <= 0:
        '''
        if block 1 touch left wall, its velocity reverses, 
        '''
        v2 = reverse_vel(v2)
        # if sound_reverse:
        # click_sound.play()
        # sound_reverse = False
        Collisions += 1
    pygame.draw.rect(win, blue, (x2, y2, s2, s2))
    pygame.draw.rect(win, red, (t, y1, s1, s1))

    pygame.display.update()
    win.fill((255, 255, 255))

pygame.quit()

Problem 1:

For the smaller value of power simulation is working fine. But for values of power>=3 animation of block 2(blue block) is getting weird(as shown in image) and I am unable to find the cause and fix it. enter image description here

As seen in the image blue block is always is this position with constantly disappearing and apprearing.

UPDATE: Number of collisions taking place are Integral part of this program. In any case number of collisions taking place shouldn't be affected

Upvotes: 1

Views: 583

Answers (2)

Rabbid76
Rabbid76

Reputation: 210968

If the velocity (v1, v2) is greater than 1, then the collision doesn't happen exactly at the new position of the object (after v1 respectively v2) was added to the position. The collision happens somewhere on the track from x1 to x1+v1 respectively x2 to x2+v2.

Split the calculation int steps, where each step is less than 1 (pixel) and do the calculations in a loop. Note the computations are done with floating point values rather than integral values:

steps = max(abs(int(v1))+1, int(abs(v2))+1)
for i in range(steps):

    # biger block
    step_v1 = v1 / steps
    x1 += step_v1
    # [...]

    # smaller Block
    step_v2 = v2 / steps
    x2 += step_v2
    # [...]

Limit the positions of the small block after an collision:

if x1 <= x2+s2:
    x2 = x1-s2
if x2 <= 0:
    x2 = 0

Since the coordinates of the blocks are floating point values, then have to be round() to integral values before use in pygame.draw.rect():

pygame.draw.rect(win, blue, (round(x2), round(y2), s2, s2))
pygame.draw.rect(win, red, (round(x1), round(y1), s1, s1))

Main loop and example with enter: 4

Collisions = 0  # counting number of collisions
run = True
while run:

    message_to_print('collision ' + str(Collisions), (0, 0, 0))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    steps = max(abs(int(v1))+1, int(abs(v2))+1)
    for i in range(steps):

        # biger block
        step_v1 = v1 / steps
        x1 += step_v1

        if x1 <= x2+s2:
            x2 = x1-s2
            v2_temp = exchange_vel(v2, m2, v1, m1)
            v1_temp = exchange_vel(v1, m1, v2, m2)
            v2, v1 = v2_temp, v1_temp
            Collisions += 1

        # smaller Block
        step_v2 = v2 / steps
        x2 += step_v2
        if x2 <= 0:
            x2 = 0
            v2 = reverse_vel(v2)
            Collisions += 1

    pygame.draw.rect(win, blue, (round(x2), round(y2), s2, s2))
    pygame.draw.rect(win, red, (round(x1), round(y1), s1, s1))

    pygame.display.update()
    win.fill((255, 255, 255))

Upvotes: 2

user10647398
user10647398

Reputation:

I understand what you are doing. You can achieve this by simply adding a else statement when you are stopping block 1 from moving out of the windows.

if x1 >= s2:
    t = x1
    t2 = x2
else:
    t2 = 0

and changing

pygame.draw.rect(win, blue, (t2, y2, s2, s2)) # x2-> t2

Explanation : When your block1 coordinate is greater than s2, then they will have normal movements but when this is not the case simply fix the coordinate of smaller block to 0.

Recommendation: Since practically during this time there will be collisions taking place, to show this in simulation you can do something like this.

    if x1 >= s2 +2:   # this prevents block 1 from moving out of window
    t = x1
    t2 = x2
else:
    t2 = z%2
z+=1

This will create a mini collision effect. I have used 2 is arbitrary choice. Also Initialize z=0 before main loop

Upvotes: 2

Related Questions