Reputation: 109
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.
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
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
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