Physics collisions on tiles

Hi,

I am adapting Ed’s “they need to be fed example” and trying to make a platform game using tiles.  The problem I am having is that the collision is being fired (ended) with every new tile, which makes sense, but it is causing an issue .  

The issue occurs when trying to figure out if the “player” is grounded or not.  In the example below you can see the wheel goes from being “grounded” to “not grounded” as it moves across the tiles.  I want the “player” to behave differently depending on whether it is on the ground or not (similar to the ‘foot’ in Ed’s example).  Can someone point out where I am going wrong, or if there is a different approach I should be using.

Thanks,

Craig

----------------------------------------------------------------------------------------- -- -- main.lua -- ----------------------------------------------------------------------------------------- -- hide status bar display.setStatusBar( display.HiddenStatusBar ) display.setDefault( "background", 197/255, 224/255, 220/255 ) local widget = require "widget" system.activate( "multitouch" ) local physics = require "physics" --physics.setDrawMode( "hybrid" ) physics.start() physics.setGravity( 0, 9.8 ) -- constants local screenW, screenH = display.actualContentWidth, display.actualContentHeight local centerX, centerY = display.contentCenterX, display.contentCenterY local originX, originY = display.screenOriginX, display.screenOriginY -- variables local horizontal = 0 local vertical = 0 local level = display.newGroup() ----------------------------------------------------------------------------------------- -- draw the block tiles for i = 1, 50 do local ground = display.newRect( 0, 0, 50, 50 ) ground:setFillColor( 1, 1, 1) ground.strokeWidth = 3 ground:setStrokeColor( 1, 0, 0 ) ground.x, ground.y = i\*50 - 100, originY + screenH physics.addBody( ground, "static", { density=1, friction=0.3, bounce=0 } ) level:insert( ground ) end local wheel1 = display.newImageRect( "wheel.png", 30, 30 ) wheel1.x, wheel1.y = 100, 100 physics.addBody( wheel1, "dynamic", { density=1, friction=5, bounce=0.2, filter={ groupIndex=-1 }, radius=15 } ) wheel1.collision = function( self, event ) -- Abort early if player has been removed somehow if( self.removeSelf == nil ) then return true end -- Detect if wheel is on the ground if( event.phase == "began" ) then print( "grounded" ) elseif( event.phase == "ended" ) then print( "not grounded" ) end return true end wheel1:addEventListener( "collision" ) level:insert( wheel1 ) ----------------------------------------------------------------------------------------- local function controlHandler( event ) local id = event.target.id local phase = event.phase if phase == "began" or phase == "moved" then if id == "forward" then wheel1:applyTorque( 10 ) elseif id == "backward" then wheel1:applyTorque( -10 ) end end end local btnForward = widget.newButton{ defaultFile = "btn\_arrow.png", width=50, height=50, id="forward", onEvent=controlHandler } btnForward.x = originX + screenW - btnForward.height \* 0.5 - 5 btnForward.y = originY + screenH - btnForward.height \* 0.5 - 5 local btnBackward = widget.newButton{ defaultFile = "btn\_arrow.png", width=50, height=50, id="backward", onEvent=controlHandler } btnBackward.rotation = 180 btnBackward.x = btnForward.x - btnForward.width - 5 btnBackward.y = btnForward.y

After doing some more digging, I came up with a solution, however I am not sure if it is the most eloquent, it seems to me there would be a lot of overhead each frame??.  So please feel free to correct me or point me in another direction.

First I assign an id to the ground object (ground.id = “platform”) and then I use “queryRegion” to check if there is an object with that id just below the wheel.
 

onEnterFrame = function( self ) local wheelGrounded = false local hits = physics.queryRegion( wheel1.x - 15, wheel1.y + 15, wheel1.x + 15, wheel1.y + 20 ) if ( hits ) then for i,v in ipairs( hits ) do if v.id == "platform" then wheel1Grounded = true end end end if wheel1Grounded then print("wheel1 is grounded") else print("wheel1 is not grounded") end end wheel1.enterFrame = onEnterFrame -- enterFrame event Runtime:addEventListener( "enterFrame", wheel1 )

Cheers,

Craig

Hi,

In case anyone comes across this in a search and uses it, I found an issue with queryRegion.  It seems to return false positives when testing on transparent sections of objects - in other words, in my case it still returned the ID of the image even though I was testing the transparent section of the image.  

I am still playing around, but rayCast doesnt seem to have the same issue.  I am drawing a line straight down from the wheel to test the area below the wheel. Here is my code.

local hits = physics.rayCast( self.wheel1.x, self.wheel1.y, self.wheel1.x, self.wheel1.y + 25 , "sorted") local terrain = {} if ( hits ) then for i,v in ipairs( hits ) do if v.object.id and v.object.id:sub( 1 ,8 ) == "platform" then -- do something end end end

Cheers,

Craig

I’d recommend using an isGrounded counter instead of an on/off flag, because when you use an on/off flag with began/ended collision sensing, each time you end a collision, it’ll be set to off whether or not it’s the last ground you were on. This results in the problem you’re seeing. Here’s a better approach:

player.isGrounded = 0 -- IN COLLISION LISTENER if "began" == event.phase and event.other.isAGroundingThing then player.isGrounded = player.isGrounded + 1 elseif "ended" == event.phase and event.other.isAGroundingThing then player.isGrounded = player.isGrounded - 1 end -- LATER, TO SEE IF PLAYER IS GROUNDED if player.isGrounded \> 0 then print("I'm grounded! Whoopee!") end

Try that and see if it helps.

  • Caleb

Thanks Caleb,

That is a much better method…

Craig

After doing some more digging, I came up with a solution, however I am not sure if it is the most eloquent, it seems to me there would be a lot of overhead each frame??.  So please feel free to correct me or point me in another direction.

First I assign an id to the ground object (ground.id = “platform”) and then I use “queryRegion” to check if there is an object with that id just below the wheel.
 

onEnterFrame = function( self ) local wheelGrounded = false local hits = physics.queryRegion( wheel1.x - 15, wheel1.y + 15, wheel1.x + 15, wheel1.y + 20 ) if ( hits ) then for i,v in ipairs( hits ) do if v.id == "platform" then wheel1Grounded = true end end end if wheel1Grounded then print("wheel1 is grounded") else print("wheel1 is not grounded") end end wheel1.enterFrame = onEnterFrame -- enterFrame event Runtime:addEventListener( "enterFrame", wheel1 )

Cheers,

Craig

Hi,

In case anyone comes across this in a search and uses it, I found an issue with queryRegion.  It seems to return false positives when testing on transparent sections of objects - in other words, in my case it still returned the ID of the image even though I was testing the transparent section of the image.  

I am still playing around, but rayCast doesnt seem to have the same issue.  I am drawing a line straight down from the wheel to test the area below the wheel. Here is my code.

local hits = physics.rayCast( self.wheel1.x, self.wheel1.y, self.wheel1.x, self.wheel1.y + 25 , "sorted") local terrain = {} if ( hits ) then for i,v in ipairs( hits ) do if v.object.id and v.object.id:sub( 1 ,8 ) == "platform" then -- do something end end end

Cheers,

Craig

I’d recommend using an isGrounded counter instead of an on/off flag, because when you use an on/off flag with began/ended collision sensing, each time you end a collision, it’ll be set to off whether or not it’s the last ground you were on. This results in the problem you’re seeing. Here’s a better approach:

player.isGrounded = 0 -- IN COLLISION LISTENER if "began" == event.phase and event.other.isAGroundingThing then player.isGrounded = player.isGrounded + 1 elseif "ended" == event.phase and event.other.isAGroundingThing then player.isGrounded = player.isGrounded - 1 end -- LATER, TO SEE IF PLAYER IS GROUNDED if player.isGrounded \> 0 then print("I'm grounded! Whoopee!") end

Try that and see if it helps.

  • Caleb

Thanks Caleb,

That is a much better method…

Craig