Making a box in Pymunk fall over

Viewed 42

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?

0 Answers
Related