I'm am new to pymunk and I wanted to make a small simulation in which a ball hits a pile of boxes like on a shooting gallery. The simulation works, but the boxes are behaving weird. They don't fall over, as you would expect but just slide. Also they glitch through the walls and each other sometimes. Maybe it is because I'm drawing them wrong in pygame. I don't really understand how pymunk.Poly.create_box() places the box in relation to the given position.
Here is my full code:
import os
import pymunk
import pygame
import numpy as np
pygame.init()
pygame.display.set_caption('Shooting gallery')
################## Globals ######################
FONT = pygame.font.SysFont('Cambria', 20)
CLOCK = pygame.time.Clock()
SCREEN_WIDTH =800
SCREEN_HEIGHT = 800
SCREEN = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
SPACE = pymunk.Space()
SPACE.gravity = [0,-981]
FPS = 60
######## Functions needed for Classes ###########
def convert_coordiantes(pos):
'''convert from pymunk coordinates to pygame coordinates'''
return [int(round(pos[0])),int(round(SCREEN_HEIGHT-pos[1]))]
################## Classes ######################
class Ball:
def __init__(self, pos, vel, radius=10, mass=60):
self.radius = radius
self.mass = mass
# pymunk stuff
self.moment = pymunk.moment_for_circle(self.mass,0,self.radius)
self.body = pymunk.Body(self.mass,self.moment)
self.body.position = pos
self.body.velocity = vel
self.shape = pymunk.Circle(self.body, self.radius)
self.shape.elasticity = 1
self.shape.friction = 0.8
self.shape.density = 1
SPACE.add(self.body, self.shape)
def draw(self):
pos1 = convert_coordiantes(self.body.position)
pygame.draw.circle(SCREEN, (255,0,0), pos1, self.radius)
class Box:
def __init__(self, pos, vel, width, height, mass=1):
self.width = width
self.height = height
self.mass = mass
# pymunk stuff
self.moment = pymunk.moment_for_box(mass, [width, height])
self.body = pymunk.Body(self.mass, self.moment)
self.body.position = pos
self.body.velocity = vel
self.shape = pymunk.Poly.create_box(self.body, [width, height], radius=2)
self.shape.elasticity = 0
self.shape.friction = 0.8
self.shape.density = 1
SPACE.add(self.body, self.shape)
def draw(self):
pos1 = convert_coordiantes(self.body.position)
pos = convert_coordiantes([self.body.position[0]-self.width/2, self.body.position[1]+self.height/2])
pygame.draw.rect(SCREEN, (100,100,100), [pos[0], pos[1], self.width, self.height])
class Segment:
def __init__(self, start, end):
self.start = start
self.end = end
self.body = pymunk.Body(body_type=pymunk.Body.STATIC)
self.shape = pymunk.Segment(self.body, start,end,1)
SPACE.add(self.body, self.shape)
def draw(self):
pygame.draw.line(SCREEN, (0,0,0), convert_coordiantes(self.start), convert_coordiantes(self.end),5)
################# Objects #######################
BOXES = [
Box([400,225], [0,0], 25, 50),
Box([430,225], [0,0], 25, 50),
Box([460,225], [0,0], 25, 50),
Box([490,225], [0,0], 25, 50),
Box([415,275], [0,0], 25, 50),
Box([445,275], [0,0], 25, 50),
Box([465,275], [0,0], 25, 50),
Box([430,325], [0,0], 25, 50),
Box([460,325], [0,0], 25, 50),
Box([445,375], [0,0], 25, 50),
]
BALLS = [
Ball([20,300], [1000,200])
]
BALLS[0].shape.elasticity = 0
TABLE = Segment([380,200], [510,200])
walls = [
[[0,0],[SCREEN_WIDTH,0]],
[[SCREEN_WIDTH,0],[SCREEN_WIDTH,SCREEN_HEIGHT]],
[[SCREEN_WIDTH,SCREEN_HEIGHT],[0,SCREEN_HEIGHT]],
[[0,SCREEN_HEIGHT],[0,0]],
]
for start_pos, end_pos in walls:
wall_body = pymunk.Body(body_type=pymunk.Body.STATIC)
wall_shape = pymunk.Segment(wall_body, start_pos, end_pos, 1)
wall_shape.elasticity = 1
wall_shape.friction = 1
SPACE.add(wall_body, wall_shape)
################ Functions ######################
def draw():
pygame.event.pump()
SCREEN.fill((200,200,200))
[box.draw() for box in BOXES]
[ball.draw() for ball in BALLS]
for start_pos, end_pos in walls:
pygame.draw.line(SCREEN, (0,0,0), convert_coordiantes(start_pos), convert_coordiantes(end_pos),5)
TABLE.draw()
# update
pygame.display.update()
if __name__ == '__main__':
# main pygame loop
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
draw()
CLOCK.tick(FPS)
SPACE.step(1/FPS)
Can someone explain why my boxes aren't falling over and how I could achieve that?