Show PyMunk with PyGame - Python

4k views Asked by At

I am trying to learn PyMunk and I used their basic example from the website:

import pymunk

space = pymunk.Space()
space.gravity = 0,-1000

body = pymunk.Body(1,1666)
body.position = 50,100

poly = pymunk.Poly.create_box(body)
space.add(body, poly)

while True:
    space.step(0.02)

But it does not create a window, does not show anything. How to use PyGame to create the graphical window?

2

There are 2 answers

2
viblo On BEST ANSWER

What that example does is create a simulation, add a box shaped object inside and then run the simulation infinitely. The code doesn't print or draw anything, so you will not actually see the output. To get a better understanding and something on screen I suggest you start with the tutorial: http://www.pymunk.org/en/latest/tutorials/SlideAndPinJoint.html

Pymunk is a 2d rigid body physics library, which means that what it does is simulate how objects move and interact with each other in 2 dimensions. Its not made for drawing to the screen or read input.

You can of course use it as is without anything else, and just print out the result of the simulation. But more common is that you want to draw to the screen, read input and so on. One way to do that is by using the game library Pygame that helps out with drawing to the screen, reading input, having a game loop and so on.

Pymunk itself does have some helper functions so that you can easily connect it with Pygame (and a couple of other libraries), but this is not the core part. Usually these helper functions are good for when you want something quick-n-dirty such as a prototype and you don't have need to customize the drawing.

Now, this said, if you want to see something you can add a print statement to the while loop, so it becomes like this:

while True:
    space.step(0.02)
    print(body.position)

Then it will print out the position of the ball each step of the simulation, and you can see that its changing all the time (because of the gravity that is set on the space).

There are more advanced examples included in Pymunk that are both interactive and show something on screen. These examples depends on mostly either Pygame or Pyglet, but the principle is the same in case you have a different library you want to use it with.

0
skrx On

Here's an example that shows how I use Pymunk in combination with pygame. The Entity class is a pygame.sprite.Sprite subclass to which I attach a pymunk.Body and a pymunk.Shape as well as a reference to the pm.Space, so that the bodies and shapes can be added and removed from it. The position of the sprite's rect gets set to the self.body.position each frame, so that we get the correct blit position for the self.image and can simply draw all sprites by calling self.sprite_group.draw(self.screen).

import math

import pygame as pg
import pymunk as pm
from pymunk import Vec2d


def flipy(p):
    """Convert chipmunk coordinates to pygame coordinates."""
    return Vec2d(p[0], -p[1]+600)


class Entity(pg.sprite.Sprite):

    def __init__(self, pos, space):
        super().__init__()
        self.image = pg.Surface((46, 52), pg.SRCALPHA)
        pg.draw.polygon(self.image, (0, 50, 200),
                        [(0, 0), (48, 0), (48, 54), (24, 54)])
        self.orig_image = self.image
        self.rect = self.image.get_rect(topleft=pos)
        vs = [(-23, 26), (23, 26), (23, -26), (0, -26)]
        mass = 1
        moment = pm.moment_for_poly(mass, vs)
        self.body = pm.Body(mass, moment)
        self.shape = pm.Poly(self.body, vs)
        self.shape.friction = .9
        self.body.position = pos
        self.space = space
        self.space.add(self.body, self.shape)

    def update(self, dt):
        pos = flipy(self.body.position)
        self.rect.center = pos
        self.image = pg.transform.rotate(
            self.orig_image, math.degrees(self.body.angle))
        self.rect = self.image.get_rect(center=self.rect.center)
        # Remove sprites that have left the screen.
        if pos.x < 20 or pos.y > 560:
            self.space.remove(self.body, self.shape)
            self.kill()

    def handle_event(self, event):
        if event.type == pg.KEYDOWN:
            if event.key == pg.K_a:
                self.body.angular_velocity = 5.5
            elif event.key == pg.K_w:
                self.body.apply_impulse_at_local_point(Vec2d(0, 900))


class Game:

    def __init__(self):
        self.done = False
        self.clock = pg.time.Clock()
        self.screen = pg.display.set_mode((800, 600))
        self.gray = pg.Color('gray68')
        self.red = pg.Color('red')

        # Pymunk stuff.
        self.space = pm.Space()
        self.space.gravity = Vec2d(0.0, -900.0)
        self.static_lines = [
            pm.Segment(self.space.static_body, (60, 100), (370, 100), 0),
            pm.Segment(self.space.static_body, (370, 100), (600, 300), 0),
            ]
        for lin in self.static_lines:
            lin.friction = 0.8
        self.space.add(self.static_lines)
        # A sprite group which holds the pygame.sprite.Sprite objects.
        self.sprite_group = pg.sprite.Group(Entity((150, 200), self.space))

    def run(self):
        while not self.done:
            self.dt = self.clock.tick(30) / 1000
            self.handle_events()
            self.run_logic()
            self.draw()

    def handle_events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.done = True
            if event.type == pg.MOUSEBUTTONDOWN:
                self.sprite_group.add(Entity(flipy(event.pos), self.space))
            for sprite in self.sprite_group:
                sprite.handle_event(event)

    def run_logic(self):
        self.space.step(1/60)  # Update physics.
        self.sprite_group.update(self.dt)  # Update pygame sprites.

    def draw(self):
        self.screen.fill(pg.Color(140, 120, 110))
        for line in self.static_lines:
            body = line.body
            p1 = flipy(body.position + line.a.rotated(body.angle))
            p2 = flipy(body.position + line.b.rotated(body.angle))
            pg.draw.line(self.screen, self.gray, p1, p2, 5)
        self.sprite_group.draw(self.screen)
        # Debug draw. Outlines of the Pymunk shapes.
        for obj in self.sprite_group:
            shape = obj.shape
            ps = [pos.rotated(shape.body.angle) + shape.body.position
                  for pos in shape.get_vertices()]
            ps = [flipy((pos)) for pos in ps]
            ps += [ps[0]]
            pg.draw.lines(self.screen, self.red, False, ps, 1)

        pg.display.flip()


if __name__ == '__main__':
    pg.init()
    Game().run()
    pg.quit()