pymunk - How to restrict movement?

1.2k views Asked by At

I'm making a pong clone with pymunk in order to learn how the lib works. I got the ball bouncing off the walls correctly, but the paddle still refuses to stay inside the rectangle defined by the segments, one on each side of the screen.

def handle_input(self):
    keys = pygame.key.get_pressed()
    if keys[K_UP]: return Vec2d(0, 200)
    elif keys[K_DOWN]: return Vec2d(0, -200)
    else: return Vec2d(0, 0)

This function detects if K_UP or K_DOWN keys are pressed. If so, it returns a new vector with the desired velocity, which is then assigned to paddle.body.velocity. The problem is, when the paddle reaches the top or bottom of the screen, instead of halting on those coordinates, it goes a little bit further up (or down) until the respective key is released, at which point it slowly returns in the opposite direction. The segment seems to offer some kind of resistance to the paddle, but only manages to stop it halfway out of the screen.

out

Why is this happening? How can I restrict the paddle's movement so that it only moves within the bounds established by the surrounding segments?


Source: game.py | paddle.py

2

There are 2 answers

2
viblo On

The problem is that you set the velocity directly on the body each frame. That will create problems for the collision solver and allow the paddle to move through the wall. Either you change it so that you apply an impulse instead, or you restrict its movement in another way.

I have a similar example in the examples folder of pymunk, breakout.py There I used a GrooveJoint to restrict its movement:

player_body = pymunk.Body(500, pymunk.inf)
player_shape = pymunk.Circle(player_body, 35)
player_shape.color = THECOLORS["red"]
player_shape.elasticity = 1.0
player_body.position = 300,100
# restrict movement of player to a straigt line 
move_joint = pymunk.GrooveJoint(space.static_body, player_body, (100,100), (500,100), (0,0))
space.add(player_body, player_shape, move_joint) 

Full code here: https://github.com/viblo/pymunk/blob/master/examples/breakout.py

Note that setting the velocity every frame might have other side effects, but in your case I think it should work fine, just as the breakout example.

1
viblo On

One solution is to add some detection when the paddle reaches the top / bottom walls and if hit restrict the velocity to only be allowed in the opposite direction.

To do that you could create a collision handler between the walls and the paddle. Then in the begin function set a variable touching_top / touching_bottom on the paddle, and on separate set the variable to false.

Here is a basic example on a paddle that get its velocity set every frame until it reaches the top using this technique:

import pymunk

space = pymunk.Space()

top = pymunk.Segment(space.static_body, (0,100), (100,100), 1)
space.add(top)
top.collision_type = 1

b = pymunk.Body(100,1000)
b.position = (50,50)
paddle = pymunk.Poly.create_box(b, (5,20))
paddle.collision_type = 2
space.add(b, paddle)

touching_top = False

def begin(space, arbiter, *args, **kwargs):
    global touching_top
    touching_top = True
    print "begin"
    return True

def separate(space, arbiter, *args, **kwargs):
    global touching_top
    touching_top = False
    print "separate"

space.add_collision_handler(1, 2, begin=begin, separate=separate)

for x in range(100):
    print paddle.body.position

    if not touching_top:
        paddle.body.velocity = (0,100)

    space.step(1/60.)