Poly-Poly collision bug (using Pymunk 4.0.0)

Discuss any Chipmunk bugs here.
Post Reply
icefox
Posts: 2
Joined: Sun Jul 06, 2014 5:45 pm
Contact:

Poly-Poly collision bug (using Pymunk 4.0.0)

Post by icefox »

I had a very weird bug using Pymunk, where dropping a box made out of a Poly atop a static platform made out of a Poly would result in the box falling right through the platform and start spinning. It would do this when hitting the platform from any direction except the -X direction (going left to right), which seemed to work fine. Giving the platform radius=1 made the box stop falling through, but instead it rolls off to one side of its own free will. This doesn't happen using a Circle for the box, or line segments to make the platform. A video of this behavior is here:

https://www.youtube.com/watch?v=vKZz1ZpO-24

As far as I know, I'm doing everything fine. There are no forces on the box in the above video apart from gravity. The box2d_vertical_stack.py demo works perfectly, as do all the other demos. My code really doesn't have much going on in it, so I'm pretty sure that's not the cause; an example is below. So really I have no clue what on earth is going on here.

Code: Select all

import pyglet
import pymunk
import pymunk.pyglet_util

import os


class Actor(object):
    """The basic thing-that-moves-and-does-stuff in a `Room`."""
    def __init__(s, x, y):
        s.verts = [
            (-10, -10), (-10, 10),
            (10, 10), (10, -10)
        ]
        s.body = pymunk.Body(1, 200)
        #s.shape = pymunk.Circle(s.body, 10)
        s.shape = pymunk.Poly(s.body, s.verts, radius=1)
        s.shape.friction = 0.8
        s.body.position = (x,y)

    def draw(s):
        pymunk.pyglet_util.draw(s.shape)

STATIC_BODY = pymunk.Body()

class Terrain(object):
    def __init__(s, verts, colors, batch=None):
        "Create a `Terrain` object."
        s.verts = verts
        s.colors = colors
        s.body = STATIC_BODY
        poly = pymunk.Poly(s.body, verts)
        poly.friction = 0.8
        line = pymunk.Segment(s.body, verts[0], verts[1], 2)
        s.shapes = [poly]

        s.batch = None or pyglet.graphics.Batch()

    def addToSpace(s, space):
        space.add(s.shapes)
        if s.body != STATIC_BODY:
            space.add(s.body)

def createBlock(x, y, w, h, color=(255, 255, 255, 255), batch=None):
    "Creates a `Terrain` object representing a block of the given size."
    xf = float(x)
    yf = float(y)
    wf = float(w)
    hf = float(h)
    verts = [
        (xf, yf), (xf+wf, yf), 
        (xf+wf, yf), (xf+wf, yf+hf),
        (xf+wf, yf+hf), (xf, yf+hf),
        (xf, yf+hf), (xf, yf)
    ]
    colors = []
    if isinstance(color, list):
        if len(color) != len(verts):
            raise Exception("Color array not right size, expected {}, got {}: {}".format(len(verts), len(color), color))
        else:
            colors = color
    else:
        if len(color) != 4:
            raise Exception("color is not a 4-tuple: {}".format(color))
        else:
            colors = [color] * len(verts)

    t = Terrain(verts, colors, batch)
    return t

class Room(object):
    """A collection of `Terrain` objects and environmental data.
Also handles physics.  There's only ever one `Room` on screen
at a time."""
    def __init__(s):
        s.terrain = set()
        s.space = pymunk.Space()
        s.space.gravity = (0.0, -400.0)

        s.actors = set()

        s.name = ""

    def addTerrain(s, t):
        "Adds a `Terrain` object to the room."
        s.terrain.add(t)
        s.space.add(t.shapes)

    def addActor(s, a):
        "Adds the given `Actor` to the room."
        s.actors.add(a)
        s.space.add(a.shape, a.body)

    def update(s,dt):
        "Updates all the physics objects in the room."
        s.space.step(dt)


    def draw(s):
        "Draws the room and all its contents."
        for ter in s.terrain:
            ter.draw()
        for act in s.actors:
            act.draw()

class World(object):
    """Contains all the state for the game."""
    def __init__(s, screenw, screenh):
        s.window = pyglet.window.Window(width=screenw, height=screenh)
        s.screenw = screenw
        s.screenh = screenh

        s.setupWorld()

        s.physicsSteps = 30.0

        s.window.push_handlers(
            on_draw = lambda: s.on_draw(),
        )
        
    def setupWorld(s):
        s.room = Room()
        b1 = createBlock(330, 100, 570, 30)
        b2 = createBlock(300, 100, 30, 300)
        b3 = createBlock(800, 100, 30, 300)
        b4 = createBlock(300, 200, 270, 30)
        s.room.addTerrain(b1)
        s.room.addTerrain(b2)
        s.room.addTerrain(b3)
        s.room.addTerrain(b4)

        s.player = Actor(s.screenw / 2, s.screenh / 2)
        s.room.addActor(s.player)


    def update(s, dt):
        step = dt / s.physicsSteps
        for _ in range(int(s.physicsSteps)):
            s.room.update(step)

    def on_draw(s):
        s.window.clear()
        pymunk.pyglet_util.draw(s.room.space)
   

PHYSICS_FPS = 60.0
def main():
    screenw = 1024
    screenh = 768

    world = World(screenw, screenh)

    pyglet.clock.schedule_interval(lambda dt: world.update(dt), 1.0/PHYSICS_FPS)
    pyglet.app.run()


if __name__ == '__main__':
    main()

icefox
Posts: 2
Joined: Sun Jul 06, 2014 5:45 pm
Contact:

Re: Poly-Poly collision bug (using Pymunk 4.0.0)

Post by icefox »

Well, a friend helped me figure out the problem... I'm passing line segments in to Poly(), not corners of the poly, so instead of a square it's really making an octagon with four sides of length 0. Presumably this causes some excitement when it comes time to calculate the normals for those sides, or something. It might not be a bad idea to have a check for this in Chipmunk, when you have it compiled in debug mode at least.

Still, it's entertaining to watch the video.
viblo
Posts: 206
Joined: Tue Aug 21, 2007 3:12 pm
Contact:

Re: Poly-Poly collision bug (using Pymunk 4.0.0)

Post by viblo »

Good to hear you figured it out!

Would not be hard to add a check if you add the same point twice.. But it wont cover all cases, its enough if one point lies on the line between to other to create the problem:

Code: Select all

verts = [
        (x, y),
        (x+w, y),
        (x+w, y+h), (x+w/2., y+h),
        (x, y+h)
    ]
There is a check in chipmunk if a polygon is convex, I think the best would be to expand it to also check for this case.. But it can also be handled by pymunk. You dont create polygons that often usually so the performance should not be an issue.
http://www.pymunk.org - A python library built on top of Chipmunk to let you easily get cool 2d physics in your python game/app
Post Reply

Who is online

Users browsing this forum: No registered users and 5 guests