I'm trying to create a simulation for a conveyor belt with objects on it, to see how the behaviour would be. I am playing around but struggling with the functionality of the conveyor belt. Right now, I try to add impulse or velocity to the objects whenever they have a collision with the belt, but I have yet to get a good result. Here is just an example of how I tried to give impulse to the objects, however, whenever I add a new object (with mouse event), the new impulse only applies to the newest object.
import pygame
import pymunk
import pymunk.pygame_util
import math
import sys
pygame.init()
space = pymunk.Space()
WIDTH, HEIGHT = 1920,800
mu = 2
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Kinematics approximation ")
def calculate_distance(p1, p2):
return math.sqrt((p2[1] - p1[1])**2 + (p2[0] - p1[0])**2)
def calculate_angle(p1, p2):
return math.atan2(p2[1] - p1[1], p2[0] - p1[0])
def draw(space, window,draw_options):
window.fill("white")
space.debug_draw(draw_options)
pygame.display.update()
def create_belt(space):
belts = [
[(0,500), (600,500), 5],
[(600,500), (800,400), 6],
[(800,400), (WIDTH,400), 6]
]
for pos_1, pos_2, width in belts:
shape = pymunk.Segment(space.static_body,pos_1, pos_2, width)
shape.body.position = 0,0
shape.friction = mu
space.add(shape)
def create_object(space, mass, pos):
body = pymunk.Body(body_type = pymunk.Body.DYNAMIC)
body.position = pos
shape = pymunk.Poly.create_box(body, size = (20,10))
shape.mass = mass
shape.color = (255,0,0, 100)
shape.body.friction = mu
#shape.body.velocity = (80,0)
space.add(body,shape)
return shape
def run(window, width, height):
run = True
clock = pygame.time.Clock()
fps = 240
dt = 1/fps
space = pymunk.Space()
space.gravity = (0,981)
create_belt(space)
def coll_begin(arbiter, space, data):
print(arbiter)
#angle = calculate_angle(*)
#force = calculate_distance(*line) * 50
#fx = math.cos(angle) * force
#fy = math.sin(angle) * force
return True
def coll_pre(arbiter, space, data):
for object in objects:
object.body.apply_impulse_at_local_point((12, 0),(0,0))
return True
def coll_post(arbiter, space, data):
#print(velocity_at_local_point)
pass
def coll_separate(arbiter, space, data):
pass
handler = space.add_default_collision_handler()
handler.begin = coll_begin
handler.pre_solve = coll_pre
handler.post_solve = coll_post
handler.separate = coll_separate
draw_options = pymunk.pygame_util.DrawOptions(window)
pressed_pos = None
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
pressed_pos = pygame.mouse.get_pos()
objects = []
objects.append(create_object(space,10, pressed_pos))
draw(space,window, draw_options)
space.step(dt)
clock.tick(fps)
pygame.quit()
sys.exit()
if __name__ == "__main__":
run(window, WIDTH, HEIGHT)
There are two problems in your code:
First, you are resetting the objects list every time a new object is created. This means that when you apply impulse to all objects in the objects list, only impulse is applied to the last object created. This can be easily fixed by moving the
objects = []line out of the game loop.After fixing this, you might have noticed that all objects are getting impulse, whether they touch the belt or not. This is caused by the following: whenever any object is colliding with the conveyor belt, you apply an impulse to all objects. This causes all objects to move forward, not only the one that collides with the conveyor belt.
You can fix the two above errors using the
Arbiter.shapestuple that is provided as an argument to thecoll_prefunction. This tuple contains the two objects colliding with eachother, and thus also the object you want to move. The second object is the new object you want to move. This change also makes theobjectslist useless, as you don't use it anymore to apply the impulse.Here's the code:
I would also recommend increasing the impulse given, as now the objects travel to slowly to resist to gravity on the inclined part of the belt. A value of 40 worked for me.