My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
SlideAndPinJointsExample  
A step by step tutorial for the demo_slide_and_pinjoint example.
Featured
Updated Nov 24, 2011 by v...@viblo.se

Slide and Pin Joint Demo Step by Step

This is a step by step tutorial explaining the demo demo_slide_and_pinjoint.py included in pymunk. It is probably a good idea to have the file near by if I miss something in the tutorial or something is unclear.

1 - Before we start

For this tutorial you will need:

  1. Python (of course)
  2. ctypes (included with Python 2.5 and later)
  3. pygame (found at www.pygame.org)
  4. pymunk
(pygame is only needed for this tutorial and some of the included demos, it is not required to run just pymunk)

Pymunk is built on top of the 2d physics library Chipmunk. Chipmunk itself is written in C meaning pymunk need to call into the c code. The ctypes library helps with this, however if you are on a platform that I haven't been able to compile it on you might have to do it yourself. The good news is that it is very easy to do!

When you have pymunk installed, try to import it from the python prompt to make sure it works and can be imported:

>>> import pymunk

If you get an error message it usually is because pymunk could not find the chipmunk library because it was not compiled (should not happen on windows and ubuntu, as pymunk ships with the code compiled for those two). To compile chipmunk, do

> python setup.py build_chipmunk
> python setup.py install

More information can be found in the readme file that is included in the pymunk distribution.

If it doesnt work, feel free to write a post in the chipmunk forum, contact me or add your problem to the issue tracker.

2 - An empty simulation

Ok, lets start. Chipmunk (and therefore pymunk) has a couple of central concepts, which is explained pretty good in this citation from the Chipmunk docs:

  • rigid bodies: A rigid body holds the physical properties of an object. (mass, position, rotation, velocity, etc.) It does not have a shape by itself. If you’ve done physics with particles before, rigid bodies differ mostly in that they are able to rotate.
  • collision shapes: By attaching shapes to bodies, you can define the a body’s shape. You can attach many shapes to a single body to define a complex shape, or none if it doesn’t require a shape.
  • constraints/joints: You can attach joints between two bodies to constrain their behavior.
  • spaces: Spaces are the basic simulation unit in Chipmunk. You add bodies, shapes and joints to a space, and then update the space as a whole.

The documentation for chipmunk can be found here: http://chipmunk-physics.net/release/ChipmunkLatest-Docs/ It is a good idea to read it, so do it now :)

The API documentation for pymunk 2.0.0 can be found here: http://pymunk.googlecode.com/svn/tags/pymunk-2.0.0/docs/api/index.html

We are now ready to write some code:

import sys
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk #1

def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 600))
    pygame.display.set_caption("Joints. Just wait and the L will tip over")
    clock = pygame.time.Clock()
    running = True
    
    space = pymunk.Space() #2
    space.gravity = (0.0, -900.0)
    
    while running:
        for event in pygame.event.get():
            if event.type == QUIT:
                running = False
            elif event.type == KEYDOWN and event.key == K_ESCAPE:
                running = False
                        
        screen.fill(THECOLORS["white"])
        
        space.step(1/50.0) #3
        
        pygame.display.flip()
        clock.tick(50)
        
if __name__ == '__main__':
    sys.exit(main())

The code will display a blank window, and will run a physics simulation of an empty space.

  1. We need to import pymunk in order to use it...
  2. We then create a space and set its gravity to something good. Remember that what is important is what looks good on screen, not what the real world value is.
  3. In our game loop we call the step() function on our space. It will do a physics step. Note: It is best to keep the stepsize constant and not adjust it depending on the framrate. The physic simulation will work much better with a constant step size.

3 - Falling balls

The easiest shape to handle (and draw) is the circle. Therefore our next step is to make a ball spawn once in while. In most demos all code is in one big pile in the main() function as they are so small and easy, but I will extract some methods in this tutorial to make it more easy to follow. First, a function to add a ball to a space:

def add_ball(space):
    mass = 1
    radius = 14
    inertia = pymunk.moment_for_circle(mass, 0, radius) # 1
    body = pymunk.Body(mass, inertia) # 2
    x = random.randint(120,380)
    body.position = x, 550 # 3
    shape = pymunk.Circle(body, radius) # 4
    space.add(body, shape) # 5
    return shape
  1. All bodies must have their moment of inertia set. If our object is a normal ball we can use the predefined function moment_for_circle to calculate it given its mass and radius.
  2. After we have the inertia we can create the body of the ball.
  3. And we set its position
  4. And in order for it to collide with things, it needs to have one (or many) collision shape(s).
  5. Finally we add the body and shape to the space to include it in our simulation.

Now that we can create balls we want to display them:

def draw_ball(screen, ball):
    p = int(ball.body.position.x), 600-int(ball.body.position.y)
    pygame.draw.circle(screen, THECOLORS["blue"], p, int(ball.radius), 2)

As I have used pygame in this example, we can use the draw.circle function to draw the balls. But first we must convert the position of the ball. We earlier set the gravity to -900 (that is, it will point down the y axis). Pygame thinks 0,0 is at the top left of the screen, with y increasing downwards. So we make a simple conversion of the y value.

With these two functions and a little code to spawn balls you should see a couple of balls falling. Yay!

import sys, random
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk

#def add_ball(space):
#def draw_ball(screen, ball):

def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 600))
    pygame.display.set_caption("Joints. Just wait and the L will tip over")
    clock = pygame.time.Clock()
    running = True
    
    space = pymunk.Space()
    space.gravity = (0.0, -900.0)
    
    balls = []
    
    ticks_to_next_ball = 10
    while running:
        for event in pygame.event.get():
            if event.type == QUIT:
                running = False
            elif event.type == KEYDOWN and event.key == K_ESCAPE:
                running = False
        
        ticks_to_next_ball -= 1
        if ticks_to_next_ball <= 0:
            ticks_to_next_ball = 25
            ball_shape = add_ball(space)
            balls.append(ball_shape)

        screen.fill(THECOLORS["white"])
        
        for ball in balls:
            draw_ball(screen, ball)
        
        space.step(1/50.0)
        
        pygame.display.flip()
        clock.tick(50)
        
if __name__ == '__main__':
    sys.exit(main())   

4 - A static L

Falling balls are quite boring. We don't see any physics simulation except basic gravity, and everyone can do gravity without help from a physics library. So lets add something the balls can land on, two static lines forming an L. As with the balls we start with a function to add an L to the space:

def add_static_L(space):
    body = pymunk.Body() # 1
    body.position = (300,300)    
    l1 = pymunk.Segment(body, (-150, 0), (255.0, 0.0), 5.0) # 2
    l2 = pymunk.Segment(body, (-150.0, 0), (-150.0, 50.0), 5.0)
            
    space.add_static(l1, l2) # 3
    return l1,l2
  1. We create a "static" body. The important step is to never add it to the space. Note how static bodies are created by not passing any arguments to the Body constructor.
  2. A line shaped shape is created here.
  3. Remember to not add the body to the shape as we want it to be static. If you promise to never move a the shapes/body you can use the add_static method, it makes the collision detection a bit faster. However, the normal add method is just as good, just a little slower and required for all objects that does move.

Next we add a function to draw the L shape:

def draw_lines(screen, lines):
    for line in lines:
        body = line.body
        pv1 = body.position + line.a.rotated(body.angle) # 1
        pv2 = body.position + line.b.rotated(body.angle)
        p1 = to_pygame(pv1) # 2
        p2 = to_pygame(pv2)
        pygame.draw.lines(screen, THECOLORS["lightgray"], False, [p1,p2])
  1. In order to get the position with the line rotation we use this calculation. line.a is the first endpoint of the line, line.b the second. At the moment the lines are static, so we don't really have to do this exatra calculation, but we will soon make them move and rotate.
  2. This is a little function to convert coordinates from pymunk to pygame world. Now that we have it we can use it in the draw_ball() function as well. We want to flip the y coordinate (-p.y), and then offset it with the screen height (+600). It looks like this:
def to_pygame(p):
    """Small hack to convert pymunk to pygame coordinates"""
    return int(p.x), int(-p.y+600)

We add a call to add_static_L() and one to draw_lines() and now we should see an inverted L shape in the middle will balls spawning and hitting the shape.

import sys, random
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk as pm
import math

#def to_pygame(p):
#def add_ball(space):
#def draw_ball(screen, ball):
#def add_static_l(space):
#def draw_lines(screen, lines):

def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 600))
    pygame.display.set_caption("Joints. Just wait and the L will tip over")
    clock = pygame.time.Clock()
    running = True
    
    space = pymunk.Space()
    space.gravity = (0.0, -900.0)
    
    lines = add_static_L(space)
    balls = []
    
    ticks_to_next_ball = 10
    while running:
        for event in pygame.event.get():
            if event.type == QUIT:
                running = False
            elif event.type == KEYDOWN and event.key == K_ESCAPE:
                running = False
        
        ticks_to_next_ball -= 1
        if ticks_to_next_ball <= 0:
            ticks_to_next_ball = 25
            ball_shape = add_ball(space)
            balls.append(ball_shape)

        screen.fill(THECOLORS["white"])
        
        for ball in balls:
            draw_ball(screen, ball)
        
        draw_lines(screen, lines)
        
        space.step(1/50.0)
        
        pygame.display.flip()
        clock.tick(50)
        
if __name__ == '__main__':
    sys.exit(main())
    

5 - Joints (1)

A static L shape is pretty boring. So lets make it a bit more exciting by adding two joints, one that it can rotate around, and one that prevents it from rotating too much. In this part we only add the rotation joint, and in the next we constrain it. As our static L shape won't be static anymore we also rename the function to add_L().

def add_L(space):
    rotation_center_body = pymunk.Body() # 1
    rotation_center_body.position = (300,300)
    
    body = pymunk.Body(10, 10000) # 2
    body.position = (300,300)    
    l1 = pymunk.Segment(body, (-150, 0), (255.0, 0.0), 5.0)
    l2 = pymunk.Segment(body, (-150.0, 0), (-150.0, 50.0), 5.0)
    
    rotation_center_joint = pymunk.PinJoint(body, rotation_center_body, (0,0), (0,0)) # 3    

    space.add(l1, l2, body, rotation_center_joint)
    return l1,l2
  1. This is the rotation center body. Its only purpose is to act as a static point in the joint so the line can rotate around it.
  2. The L shape will now be moving in the world, and therefor it can no longer have infinite mass. I have precalculated the inertia to 10000. (ok, I just took a number that worked, the important thing is that it looks good on screen!).
  3. A pin joint allow two objects to pivot about a single point. In our case one of the objects will be stuck to the world.

To make it easy to see the point we draw a little red ball in its center

        pygame.draw.circle(screen, THECOLORS["red"], (300,300), 5)

In a bigger program you will want to get the rotation_center_body.position instead of my little cheat here with (300,300), but it will work for this tutorial as the rotation center is static.

6 - Joints (2)

In the previous part we added a pin joint, and now its time to constrain the rotating L shape to create a more interesting simulation. In order to do this we modify the add_L() function:

def add_L(space):
    rotation_center_body = pymunk.Body()
    rotation_center_body.position = (300,300)
    
    rotation_limit_body = pymunk.Body() # 1
    rotation_limit_body.position = (200,300)
    
    body = pymunk.Body(10, 10000)
    body.position = (300,300)    
    l1 = pymunk.Segment(body, (-150, 0), (255.0, 0.0), 5.0)
    l2 = pymunk.Segment(body, (-150.0, 0), (-150.0, 50.0), 5.0)
    
    rotation_center_joint = pymunk.PinJoint(body, rotation_center_body, (0,0), (0,0)) 
    joint_limit = 25
    rotation_limit_joint = pymunk.SlideJoint(body, rotation_limit_body, (-100,0), (0,0), 0, joint_limit) # 2

    space.add(l1, l2, body, rotation_center_joint, rotation_limit_joint)
    return l1,l2
  1. We add a body..
  2. Create a slide joint. It behaves like pin joints but have a minimum and maximum distance.

And to make it a bit more clear, we draw a circle to do symbolize the joint with a green circle with its radius set to the joint max:

        pygame.draw.circle(screen, THECOLORS["green"], (200,300), 25, 2)

7 - The end

You might notice that we never delete balls. This will make the simulation require more and more memory and use more and more cpu, and this is of course not what we want. So in the final step we add some code to remove balls from the simulation when they are bellow the screen.

        balls_to_remove = []
        for ball in balls:
            if ball.body.position.y < 0: # 1
                balls_to_remove.append(ball) # 2
            draw_ball(screen, ball)
        
        for ball in balls_to_remove:
            space.remove(ball, ball.body) # 3
            balls.remove(ball) # 4
  1. As we already have a loop we reuse it.. Check if the body.position is less than 0
  2. If that is the case, we add it to our list of balls to remove.
  3. To remove an object from the space, we need to remove its shape and its body.
  4. And then we remove it from our list of balls.

And now, done! You should have an inverted L shape in the middle of the screen being filled will balls, tipping over releasing them, tipping back and start over. You can check demo_slide_and_pinjoint.py included in pymunk, but it doesn't follow this tutorial exactly as I factored out a couple of blocks to functions to make it easier to follow in tutorial form.

If anything is unclear, not working feel free to add a comment in the bottom of the page. If you have an idea for another tutorial you want to read, or some example code you want to see included in pymunk, please add a comment here and I will try my best to create it.

The full code for this tutorial is:

import sys, random
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk
import math

def to_pygame(p):
    """Small hack to convert pymunk to pygame coordinates"""
    return int(p.x), int(-p.y+600)

def add_ball(space):
    """Add a ball to the given space at a random position"""
    mass = 1
    radius = 14
    inertia = pymunk.moment_for_circle(mass, 0, radius, (0,0))
    body = pymunk.Body(mass, inertia)
    x = random.randint(120,380)
    body.position = x, 550
    shape = pymunk.Circle(body, radius, (0,0))
    space.add(body, shape)
    return shape

def draw_ball(screen, ball):
    """Draw a ball shape"""
    p = int(ball.body.position.x), 600-int(ball.body.position.y)
    pygame.draw.circle(screen, THECOLORS["blue"], p, int(ball.radius), 2)

def add_L(space):
    """Add a inverted L shape with two joints"""
    rotation_center_body = pymunk.Body()
    rotation_center_body.position = (300,300)
    
    rotation_limit_body = pymunk.Body() # 1
    rotation_limit_body.position = (200,300)
    
    body = pymunk.Body(10, 10000)
    body.position = (300,300)    
    l1 = pymunk.Segment(body, (-150, 0), (255.0, 0.0), 5.0)
    l2 = pymunk.Segment(body, (-150.0, 0), (-150.0, 50.0), 5.0)
    
    rotation_center_joint = pymunk.PinJoint(body, rotation_center_body, (0,0), (0,0)) 
    joint_limit = 25
    rotation_limit_joint = pymunk.SlideJoint(body, rotation_limit_body, (-100,0), (0,0), 0, joint_limit) # 3

    space.add(l1, l2, body, rotation_center_joint, rotation_limit_joint)
    return l1,l2

def draw_lines(screen, lines):
    """Draw the lines"""
    for line in lines:
        body = line.body
        pv1 = body.position + line.a.rotated(body.angle)
        pv2 = body.position + line.b.rotated(body.angle)
        p1 = to_pygame(pv1)
        p2 = to_pygame(pv2)
        pygame.draw.lines(screen, THECOLORS["lightgray"], False, [p1,p2])


def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 600))
    pygame.display.set_caption("Joints. Just wait and the L will tip over")
    clock = pygame.time.Clock()
    running = True
    
    space = pymunk.Space()
    space.gravity = (0.0, -900.0)
    
    lines = add_L(space)
    balls = []
    
    ticks_to_next_ball = 10
    while running:
        for event in pygame.event.get():
            if event.type == QUIT:
                running = False
            elif event.type == KEYDOWN and event.key == K_ESCAPE:
                running = False
        
        ticks_to_next_ball -= 1
        if ticks_to_next_ball <= 0:
            ticks_to_next_ball = 25
            ball_shape = add_ball(space)
            balls.append(ball_shape)

        screen.fill(THECOLORS["white"])
        
        balls_to_remove = []
        for ball in balls:
            if ball.body.position.y < 150:
                balls_to_remove.append(ball)
            draw_ball(screen, ball)
        
        for ball in balls_to_remove:
            space.remove(ball, ball.body)
            balls.remove(ball)
        
        draw_lines(screen, lines)
        
        pygame.draw.circle(screen, THECOLORS["red"], (300,300), 5)
        pygame.draw.circle(screen, THECOLORS["green"], (200,300), 25, 2)

        space.step(1/50.0)
        
        pygame.display.flip()
        clock.tick(50)
        
if __name__ == '__main__':
    sys.exit(main())
Comment by donny.vi...@gmail.com, Dec 26, 2008

Why not define to_pygame() like this?

def to_pygame(p):

"""Small hack to convert pymunk to pygame coordinates""" return int(p.x), int(600-p.y)

Comment by project member vb%viblo...@gtempaccount.com, Jan 11, 2009

The reason why I defined the y as -p.y + 600 was that I thought it was closer to how you think and therefor easier to understand. We want to flip the y coordinate (-p.y), and then offset it with the screen height (+600). Maybe it was a bad idea as some will wonder why it isnt written in the more optimal form 600-p.y..

Comment by monster...@gmail.com, Sep 5, 2010

Seems the pymunk implementation I found (1.0.0.2-4 from ppa:xapantu/pymunk) does not need the "math.degrees(body.angle)" and instead gives the body angle in degrees without math.degrees.

Comment by project member vb%viblo...@gtempaccount.com, Sep 6, 2010

Thanks! I've updated the text now. I must have missed it when I updated the tutorial for the newest version.

Comment by rebus123...@gmail.com, Jan 2, 2011

Excellent library. I want more examples

Comment by project member vb%viblo...@gtempaccount.com, Jan 3, 2011

Thanks :) When you say you want more, do you mean tutorials like this wiki page, or more of the code examples thats included with the source dist of pymunk?

Comment by rebus123...@gmail.com, Jan 3, 2011

On the wiki the best solution in the comments will be clarifying questions. I would like to see in all the classes, some options are not clear. And to build a complex model. And another question: Is it possible to get force (momentum) acting on a particular object (to destroy the of the object if the majority force http://www.armadillorun.com ). Thank you very much

Comment by avalan...@gmail.com, Mar 12, 2011

Why this demo over time increases CPU usage?

Comment by project member vb%viblo...@gtempaccount.com, Mar 13, 2011

rebus123: First of all, sorry for the very late response, I must have missed your very quick response on my comment. About breakable objects the answer is No, not really. You might be able to figure out a way to fake it somehow, but I cant really give much help here.

I will see if I can add something more complex on the wiki, but the bad thing with complex things are that they require a lot more maintenance than simple things :) But I will at least try to add some more examples that cover other parts that what is covered by this tutorial.

Comment by project member vb%viblo...@gtempaccount.com, Mar 13, 2011

avalan: Interesting question! However, I have not been able reproduce it, after 5 minutes it still use about the same amount of cpu as in the beginning.. How did you measure and do I need to do something special to reproduce it?

Comment by PaulJohnLeonard, Mar 26, 2011

Thanks for the tutorial, I couple of things . . .

The latest from SVN does not need this,

pymunk.init_pymunk()

Also

pm.PinJoint?(body, rotation_center_body, (0,0), (0,0))

pm should be pymunk.

Comment by project member vb%viblo...@gtempaccount.com, Mar 29, 2011

Thanks for the comment! Ive added a note in the tutorial about the init function and fixed the pm problem.

Comment by rebus123...@gmail.com, May 4, 2011

Hello v...@viblo.se . please write an example for the implementation of the rope. I think it should be a lot of lines, connections of points? thanks

Comment by kovor...@gmail.com, Oct 21, 2011

wonderful tutorial (and especially there are not many of them around)! Thank you! Where can I found the demo_slide_and_pinjoint code? Thank you, Vincenzo

Comment by kovor...@gmail.com, Oct 21, 2011

The line:

pymunk.init_pymunk()

gives me an error, I solved by commenting the line (I guess the method was eliminated).

Comment by kovor...@gmail.com, Oct 22, 2011

In all programs and examples using Pygame, I didn't know how to get rid of the window after program exit (with error . I could only kill the process, but this crashes my python.

Eventually I found that the pygame window can be closed by pygame.quit() in python command line and it works fine. That is good, I don't need to restart the python each time I run a script.

I tried to put it in the code of the 1st example (after the "while running" loop) to see if the window closes properly: for some misterious reason it doesn't work, it gives an error (video system not initialized).

Probably the comment is more pygame related, but I wrote this comment because it is relevant to the exampe here.

Comment by project member v...@viblo.se, Oct 24, 2011

Thanks for the comments! I have updated the tutorial so that it at least doesnt have the now deprecated and removed init method. I just got back from a 2 week vacation, but will try to make a more thorough review of the tutorial to make sure its up to date.

I will also try to make a new source release that contains all code including the examples which unfortunately wasnt included in the current 2.0 release package. However, you can still get them directly from svn: Either browse online here: http://code.google.com/p/pymunk/source/browse/#svn%2Ftags%2Fpymunk-2.0.0%2Fexamples Or get latest version directly of the 2.0 tag.

Comment by kovor...@gmail.com, Nov 30, 2011

Wonderful! I downloaded the examples by svn. They are great! Thank you!

Comment by pyirrli...@gmail.com, Apr 11, 2012

Excellent, fantastic library. Thank you very much.

I do convert bouncing_balls.py and balls_and_lines.py examples to used with pyIrrlicht and it is worked http://code.google.com/p/pyirrlicht/source/browse/trunk/pymunk_bouncing_balls.py http://code.google.com/p/pyirrlicht/source/browse/trunk/pymunk_balls_and_lines.py

I not sure what it is same result as with pyGame, can you check result my job?

Comment by project member v...@viblo.se, Apr 22, 2012

Oh, interesting! I have never used Irrlicht myself, but it seems like a solid engine. Tested the pymunk examples and they seems to work (except for the minor detail that space doesnt pause the simulation in pymunk_balls_and_lines so you might want to alter either the description text or implement it in the code).

Got a question also, looking at the code I see you convert floats to ints and then pass it on with irr.position2di which I first thougth was a bit unnecessary as its something pygame requires. I looked at the source of pyirrlicht and saw there's a irr.position2df method, so naturally tested with that one instead but then nothing worked. Maybe there's a known feature? Otherwise you know it now :)

Comment by pyirrli...@gmail.com, Apr 23, 2012

Thanks for answer. I fixed pause the simulation in pymunk_balls_and_lines and pymunk_slide_and_pinjoint and add floating point drawing functions, convert float to int now internal in dll.

I planned use pyMunk in game brika (brika_i.py, original was written Leonel Machava based on pyGame), where is best place for questions? Perhaps forum http://chipmunk-physics.net/forum/viewtopic.php?f=6&t=1940

Comment by project member v...@viblo.se, Apr 23, 2012

As long as they are related to pymunk feel free to just create new posts for them on the general chipmunk forum, http://chipmunk-physics.net/forum/viewforum.php?f=1 You can if you want mark them with pymunk? in the title to make it easy to spot that its pymunk related. I try to read there at least once a week, and I know there are other people there as well that use pymunk (and sometimes for common 2d physics questions the other people there can help as well).

Comment by project member v...@viblo.se, Apr 23, 2012

google code tricked me, I mean mark them with [pymunk] in the header

Comment by pyirrli...@gmail.com, Apr 25, 2012

Thanks again. I start mix bricka and pyMunk, and I have question, after my few tryings I will go to Chipmunk forum.

Sorry for my English.


Sign in to add a comment
Powered by Google Project Hosting