Collision and Physic with Vector-Drawn Polygons

I’m new and am trying to create a simple triangle-shaped object that works with the physics engine. I’m a life-long coder, but all my Mobile development so far has been with GameSalad.  

I created the triangle using display.newLine, then display.append. That *appears* to be the *only* way to create a purely polygonal object in Corona.  I then create a shape using the same coordinates (minus the duplicate at the end to close the triangle, as per the docs.)  The result, shown when I turn on Physics debug, is that the visible triangle and the collision area are offset, leading to goofy behavior.

I then see in the docs for addBody:

NOTE: You should not create physics bodies from objects created using display.newLine().

I’m aware I can create the display object with a raster graphic like a .png, and then use addBody to define a shape, but I don’t want to.  I want to create a purely polygonal shape entirely with vectors, and then use addBody to put it into the physics engine.  How can I do that if I can’t use display.newLine to create the polygon?  Is there another way?  Right now it looks like a Catch 22.  Another option would be to somehow align the display object and the collision boundary.

Thanks for the help!

Scott

…note also that if I do the same thing with a rectange, still using display.newLine/display.append and a custom shape, it works perfectly.

Here is a partial solution, but collisions are still wonky:

http://www.youtube.com/watch?v=EwXfLSKflMw

-- ============================================================= -- main.lua -- ============================================================= ---------------------------------------------------------------------- --    0. Init ---------------------------------------------------------------------- io.output():setvbuf("no") -- Don't use buffer for console messages display.setStatusBar(display.HiddenStatusBar)  -- Hide that pesky bar Runtime:hideErrorAlerts( ) ---------------------------------------------------------------------- --    1. Requires ---------------------------------------------------------------------- local physics = require "physics" physics.start() physics.setDrawMode( "hybrid" ) --physics.setGravity(0,2) ---------------------------------------------------------------------- --    2. The Good stuff ---------------------------------------------------------------------- local function createPhysicsTriangle( x, y, rotation )     local xseg = 28     local yseg = 40     local triangle = display.newLine( 0,0, xseg, -yseg )     triangle:append( 2\*xseg, 0, 0, 0)     triangle:setColor( 255, 102, 102, 255 )     triangle.width = 3          triangle:setReferencePoint( display.CenterReferencePoint)     triangle.x = x     triangle.y = y     local tx       = triangle.x     local ty       = triangle.y     local tcw      = triangle.contentWidth     local tch      = triangle.contentHeight     --local triangleShape = { 0, 0, tcw/2, -tch, tcw,0 }     local triangleShape = { 0, 0, tcw/2-4, -tch+4, tcw-8,0 } -- Makes proper body, but has weird collision properties     physics.addBody( triangle, "dynamic", {shape = triangleShape, isBullet = true } )     triangle.rotation = rotation     return triangle end local function createPeg( x, y )     local tmp = display.newCircle( x, y, 5 )     physics.addBody( tmp, "static", { radius = 5 } ) end local pegY = 160 local pegX = -24 local pegXOffset = 96 for i = 1, 5 do     createPeg( pegX + i \* pegXOffset, pegY ) end timer.performWithDelay( 500, function() createPhysicsTriangle( math.random( 50, 430 ), 20, 0 ) end, 0 )  

Hi roaminggamer.

Thanks for the help! So are you saying that aligns the areas, but still has issues with collision?  Let me give it a try.

Scott

Ha!  I basically changed nothing on my own example from yesterday, and now it’s working properly. I wonder what I changed?   I’m going to put together an example that seems to work to help others, based on your input and my working project. 

Scott

So I’ve got an answer, but I’m not sure why it works.  Thanks to roaminggamer for starting me off on the right track!

If you start the triangle at the origin, and you make the points clockwise, the collisions seem to work correctly.

If you offset the same triangle by 10,10 in x, y, the collision shape seems offset from the original shape by that much, but works.

If you define the triangle points counterclockwise, the behaviour is wonky and undefinied.

I thought maybe the reference points for the object were being re-defined by physics.addBody(), but as you can see on your terminal, they remain unchanged from before to after it is called.

So it looks like maybe you *can* define poly shapes with display.addLine/display.append, but they have to be clockwise, and they have start at the origin.  You can then move them somehwhere else.  Here’s the complete code:



– main.lua


– include Corona’s “physics” library
local physics = require “physics”
physics.setDrawMode( “hybrid” ) – see how the physic engine sees objects
physics.start();
–physics.pause()

– forward declarations and other locals
local screenW, screenH, halfW = display.contentWidth, display.contentHeight, display.contentWidth*0.5

– BEGINNING OF YOUR IMPLEMENTATION

– triangle starting (not centered) at the origin seems correctly aligned with collision shape shape
    local centeredTri = display.newLine( 0,0, 50,0)
    centeredTri:append (50,50, 0,0)

    centeredTri:setColor( 0, 255, 0, 255 )
    centeredTri.x = 50
 –   centeredTri.y = 100
    
    print (“NewRun”)
    print ("centeredTri.xReference: "…centeredTri.xReference)
    print ("centeredTri.yReference: "…centeredTri.yReference)

    centeredTriShape = { 0,0, 50,0, 50,50} – First and Last vertex should be different - docs
     physics.addBody( centeredTri, { density=1.0, friction=0.3, bounce=.1, shape=centeredTriShape })
    print ("centeredTri.xReference: "…centeredTri.xReference)
    print ("centeredTri.yReference: "…centeredTri.yReference)
    
–    print centeredTriShape.yReference

 
– triangle not starting at the origin ends up offset from collision shape
    local uncenteredTri = display.newLine( 10,10, 60,10)
    uncenteredTri:append (60,60, 10,10)
    uncenteredTri:setColor( 0, 255, 0, 255 )
    uncenteredTri.x = 125
    print ("uncenteredTri.xReference: "…uncenteredTri.xReference)
    print ("uncenteredTri.yReference: "…uncenteredTri.yReference)

    uncenteredTriShape = { 10,10, 60,10, 60,60} – First and Last vertex should be different - docs
     physics.addBody( uncenteredTri, { density=1.0, friction=0.3, bounce=.1, shape=uncenteredTriShape })
    print ("uncenteredTri.xReference: "…uncenteredTri.xReference)
    print ("uncenteredTri.yReference: "…uncenteredTri.yReference)

     
–   I tried altering the reference point below to see if I could correct the offset, but that seems to
–   …mess up the collision detection even more.  I don’t understand how reference points work yet, though,
–   …so there may still be a solution here.

–   uncenteredTri:setReferencePoint(uncenteredTri.x+10 , uncenteredTri.y+10)

– triangle with counterclockwise points seems to overlap,but has wonky collision behavior
– “The shape coordinates must be defined in clockwise order, and the resulting shape must be convex-only. The
–    first and last vertex must be different.”
http://docs.coronalabs.com/api/library/physics/addBody.html
    local counterclockwiseTri = display.newLine( 0,0, 0,50)
    counterclockwiseTri:append (50,50, 0,0)
    counterclockwiseTri:setColor( 255, 0, 0, 255 )
    counterclockwiseTri.x = 200
    print ("counterclockwiseTri.xReference: "…counterclockwiseTri.xReference)
    print ("counterclockwiseTri.yReference: "…counterclockwiseTri.yReference)

    counterclockwiseTriShape = { 0,0, 0,50, 50,50} – First and Last vertex should be different - docs
     physics.addBody( counterclockwiseTri, { density=1.0, friction=0.3, bounce=.1, shape=counterclockwiseTriShape })
    print ("counterclockwiseTri.xReference: "…counterclockwiseTri.xReference)
    print ("counterclockwiseTri.yReference: "…counterclockwiseTri.yReference)

–  Make a box to Contain our Enthusiasm        
    local bouncebox = display.newRect( 0,0, screenW, 82 )
    bouncebox:setReferencePoint( display.BottomLeftReferencePoint )
    bouncebox.x, bouncebox.y = 0, display.contentHeight
    physics.addBody( bouncebox, “static”, { friction=0.3} )
    
    local bounceboxleft = display.newRect( 0,0, 5, screenH)
    bounceboxleft:setReferencePoint( display.BottomLeftReferencePoint )
    bounceboxleft.x, bounceboxleft.y = 0, display.contentHeight
    physics.addBody( bounceboxleft, “static”, { friction=0.3} )

    local bounceboxright = display.newRect( 0,0, 5, screenH )
    bounceboxright:setReferencePoint( display.BottomLeftReferencePoint )
    bounceboxright.x, bounceboxright.y = display.contentWidth-5, display.contentHeight
        physics.addBody( bounceboxright, “static”, { friction=0.3} )
    
    for loop = 1  , 50, 1 do         – make a bouncyBall (off-screen), position it, and rotate slightly
        local bouncyBall = display.newCircle( 90, 90, 10 )
        bouncyBall.x, bouncyBall.y = 160+math.random(0,50) , -100        bouncyBall.rotation = 15
        physics.addBody( bouncyBall, { density=1.0, friction=0.3, bounce=0.9, radius = 10} )

end
 

…note also that if I do the same thing with a rectange, still using display.newLine/display.append and a custom shape, it works perfectly.

Here is a partial solution, but collisions are still wonky:

http://www.youtube.com/watch?v=EwXfLSKflMw

-- ============================================================= -- main.lua -- ============================================================= ---------------------------------------------------------------------- --    0. Init ---------------------------------------------------------------------- io.output():setvbuf("no") -- Don't use buffer for console messages display.setStatusBar(display.HiddenStatusBar)  -- Hide that pesky bar Runtime:hideErrorAlerts( ) ---------------------------------------------------------------------- --    1. Requires ---------------------------------------------------------------------- local physics = require "physics" physics.start() physics.setDrawMode( "hybrid" ) --physics.setGravity(0,2) ---------------------------------------------------------------------- --    2. The Good stuff ---------------------------------------------------------------------- local function createPhysicsTriangle( x, y, rotation )     local xseg = 28     local yseg = 40     local triangle = display.newLine( 0,0, xseg, -yseg )     triangle:append( 2\*xseg, 0, 0, 0)     triangle:setColor( 255, 102, 102, 255 )     triangle.width = 3          triangle:setReferencePoint( display.CenterReferencePoint)     triangle.x = x     triangle.y = y     local tx       = triangle.x     local ty       = triangle.y     local tcw      = triangle.contentWidth     local tch      = triangle.contentHeight     --local triangleShape = { 0, 0, tcw/2, -tch, tcw,0 }     local triangleShape = { 0, 0, tcw/2-4, -tch+4, tcw-8,0 } -- Makes proper body, but has weird collision properties     physics.addBody( triangle, "dynamic", {shape = triangleShape, isBullet = true } )     triangle.rotation = rotation     return triangle end local function createPeg( x, y )     local tmp = display.newCircle( x, y, 5 )     physics.addBody( tmp, "static", { radius = 5 } ) end local pegY = 160 local pegX = -24 local pegXOffset = 96 for i = 1, 5 do     createPeg( pegX + i \* pegXOffset, pegY ) end timer.performWithDelay( 500, function() createPhysicsTriangle( math.random( 50, 430 ), 20, 0 ) end, 0 )  

Hi roaminggamer.

Thanks for the help! So are you saying that aligns the areas, but still has issues with collision?  Let me give it a try.

Scott

Ha!  I basically changed nothing on my own example from yesterday, and now it’s working properly. I wonder what I changed?   I’m going to put together an example that seems to work to help others, based on your input and my working project. 

Scott

So I’ve got an answer, but I’m not sure why it works.  Thanks to roaminggamer for starting me off on the right track!

If you start the triangle at the origin, and you make the points clockwise, the collisions seem to work correctly.

If you offset the same triangle by 10,10 in x, y, the collision shape seems offset from the original shape by that much, but works.

If you define the triangle points counterclockwise, the behaviour is wonky and undefinied.

I thought maybe the reference points for the object were being re-defined by physics.addBody(), but as you can see on your terminal, they remain unchanged from before to after it is called.

So it looks like maybe you *can* define poly shapes with display.addLine/display.append, but they have to be clockwise, and they have start at the origin.  You can then move them somehwhere else.  Here’s the complete code:



– main.lua


– include Corona’s “physics” library
local physics = require “physics”
physics.setDrawMode( “hybrid” ) – see how the physic engine sees objects
physics.start();
–physics.pause()

– forward declarations and other locals
local screenW, screenH, halfW = display.contentWidth, display.contentHeight, display.contentWidth*0.5

– BEGINNING OF YOUR IMPLEMENTATION

– triangle starting (not centered) at the origin seems correctly aligned with collision shape shape
    local centeredTri = display.newLine( 0,0, 50,0)
    centeredTri:append (50,50, 0,0)

    centeredTri:setColor( 0, 255, 0, 255 )
    centeredTri.x = 50
 –   centeredTri.y = 100
    
    print (“NewRun”)
    print ("centeredTri.xReference: "…centeredTri.xReference)
    print ("centeredTri.yReference: "…centeredTri.yReference)

    centeredTriShape = { 0,0, 50,0, 50,50} – First and Last vertex should be different - docs
     physics.addBody( centeredTri, { density=1.0, friction=0.3, bounce=.1, shape=centeredTriShape })
    print ("centeredTri.xReference: "…centeredTri.xReference)
    print ("centeredTri.yReference: "…centeredTri.yReference)
    
–    print centeredTriShape.yReference

 
– triangle not starting at the origin ends up offset from collision shape
    local uncenteredTri = display.newLine( 10,10, 60,10)
    uncenteredTri:append (60,60, 10,10)
    uncenteredTri:setColor( 0, 255, 0, 255 )
    uncenteredTri.x = 125
    print ("uncenteredTri.xReference: "…uncenteredTri.xReference)
    print ("uncenteredTri.yReference: "…uncenteredTri.yReference)

    uncenteredTriShape = { 10,10, 60,10, 60,60} – First and Last vertex should be different - docs
     physics.addBody( uncenteredTri, { density=1.0, friction=0.3, bounce=.1, shape=uncenteredTriShape })
    print ("uncenteredTri.xReference: "…uncenteredTri.xReference)
    print ("uncenteredTri.yReference: "…uncenteredTri.yReference)

     
–   I tried altering the reference point below to see if I could correct the offset, but that seems to
–   …mess up the collision detection even more.  I don’t understand how reference points work yet, though,
–   …so there may still be a solution here.

–   uncenteredTri:setReferencePoint(uncenteredTri.x+10 , uncenteredTri.y+10)

– triangle with counterclockwise points seems to overlap,but has wonky collision behavior
– “The shape coordinates must be defined in clockwise order, and the resulting shape must be convex-only. The
–    first and last vertex must be different.”
http://docs.coronalabs.com/api/library/physics/addBody.html
    local counterclockwiseTri = display.newLine( 0,0, 0,50)
    counterclockwiseTri:append (50,50, 0,0)
    counterclockwiseTri:setColor( 255, 0, 0, 255 )
    counterclockwiseTri.x = 200
    print ("counterclockwiseTri.xReference: "…counterclockwiseTri.xReference)
    print ("counterclockwiseTri.yReference: "…counterclockwiseTri.yReference)

    counterclockwiseTriShape = { 0,0, 0,50, 50,50} – First and Last vertex should be different - docs
     physics.addBody( counterclockwiseTri, { density=1.0, friction=0.3, bounce=.1, shape=counterclockwiseTriShape })
    print ("counterclockwiseTri.xReference: "…counterclockwiseTri.xReference)
    print ("counterclockwiseTri.yReference: "…counterclockwiseTri.yReference)

–  Make a box to Contain our Enthusiasm        
    local bouncebox = display.newRect( 0,0, screenW, 82 )
    bouncebox:setReferencePoint( display.BottomLeftReferencePoint )
    bouncebox.x, bouncebox.y = 0, display.contentHeight
    physics.addBody( bouncebox, “static”, { friction=0.3} )
    
    local bounceboxleft = display.newRect( 0,0, 5, screenH)
    bounceboxleft:setReferencePoint( display.BottomLeftReferencePoint )
    bounceboxleft.x, bounceboxleft.y = 0, display.contentHeight
    physics.addBody( bounceboxleft, “static”, { friction=0.3} )

    local bounceboxright = display.newRect( 0,0, 5, screenH )
    bounceboxright:setReferencePoint( display.BottomLeftReferencePoint )
    bounceboxright.x, bounceboxright.y = display.contentWidth-5, display.contentHeight
        physics.addBody( bounceboxright, “static”, { friction=0.3} )
    
    for loop = 1  , 50, 1 do         – make a bouncyBall (off-screen), position it, and rotate slightly
        local bouncyBall = display.newCircle( 90, 90, 10 )
        bouncyBall.x, bouncyBall.y = 160+math.random(0,50) , -100        bouncyBall.rotation = 15
        physics.addBody( bouncyBall, { density=1.0, friction=0.3, bounce=0.9, radius = 10} )

end
 

Is there a better way to do this now with display.newPolygon()? I couldn’t find a way to turn a simple polygon (a triangle) into a physics body, even when loading the vertices table into the shape parameter of the physics body:

[lua]
local halfW = display.contentWidth*0.5;
local halfH = display.contentHeight*0.5;

local vertices = { 0,-100, 50,0, -50,0 }

local o = display.newPolygon( halfW, halfH, vertices )

physics.addBody(o, “dynamic”, { density = 0.1, friction = 0.8, bounce = 0, shape = vertices} )
o.strokeWidth = 10
o:setStrokeColor( 255/255, 0, 0 )[/lua]

Is there a better way to do this now with display.newPolygon()? I couldn’t find a way to turn a simple polygon (a triangle) into a physics body, even when loading the vertices table into the shape parameter of the physics body:

[lua]
local halfW = display.contentWidth*0.5;
local halfH = display.contentHeight*0.5;

local vertices = { 0,-100, 50,0, -50,0 }

local o = display.newPolygon( halfW, halfH, vertices )

physics.addBody(o, “dynamic”, { density = 0.1, friction = 0.8, bounce = 0, shape = vertices} )
o.strokeWidth = 10
o:setStrokeColor( 255/255, 0, 0 )[/lua]

I can definitely say that you should always do your coordinates in a clockwise direction when using box2d.

I can definitely say that you should always do your coordinates in a clockwise direction when using box2d.