Detecting if Body is on the Ground

Hi guys,

My latest game is a platformer and I’m having trouble with the character’s movements being responsive. Basically the game’s character can only jump and roll (left or right). My problem is that these should only be available when the character is standing on the ground (or rolling). I’m currently checking the velocity of the character’s body if the x & y values are low enough so as to assume that the character is on the ground.

The thing with this method is that the responsiveness of the game suffers. If I let the character jump twice with fast input commands, the 2nd one usually fails to be detected since the guy is still shaking a bit from the impact of the landing. Does anyone have a better solution to detecting whether a body is on the ground?

I was thinking of adding a “wrapper” body just outside the body of the character which can be a sensor. The sensor can just detect by using collision listeners whether or not the body is on the ground. One issue is that I can only listen for collision events, but when the body lifts gets off the ground (either by falling off the side by rolling, or jumping), I wouldn’t be able to detect where it is. [import]uid: 144908 topic_id: 30319 reply_id: 330319[/import]

Regarding the “wrapper” solution I stated, I just read this in the docs:

The “collision” event includes phases for “began” and “ended”, which signify the moments of initial contact and broken contact

Haven’t tested though, but this means that the wrapper solution is viable. Although this means that there’ll additional IDs to assign since now I’ll have to add a “ground” id to all the horizontal or slightly tilted ground bodies that I have so that I can check for collision events.

Anyone with a better solution still? [import]uid: 144908 topic_id: 30319 reply_id: 121484[/import]

The most common solution is essentially what you described. For some reason people in Corona seem to actually use the player body itself as the sensor but in past projects where I used box2d in it’s native environment i would add a sensor to the bottom of the character that stuck out a tiny bit. From ther eis just a simple switch of onGround = true/false.

Btw, the reason it stuck out of the bottom is that way I didn’t have to assign special IDs to ground objects. Depending on your mechanics you could do the same.
[import]uid: 147305 topic_id: 30319 reply_id: 121485[/import]

I’m having a problem a bit worse than this:

-the player can only jump if he is on the ground, as well as to your problem.
but the error occurs when the player is on two objects at the same time and when I decide to move to one side of the event “ended” is triggered but the “Began” is not, then the physics of the game registers the player is in the air and he is unable to jump.

can someone help me?
thank you, [import]uid: 95474 topic_id: 30319 reply_id: 121507[/import]

Well, in theory instead of using a boolean checker you could do a counter. so the logic would be as follows(and forgive any syntax errors)

[lua]function onGroundCollission(event)
if event.phase == “began” then
onGround = onGround +1
elseif event.phase == “ended” then
onGround = onGround -1
end

end[/lua]

then when you jump just check if onGround == 0 [import]uid: 147305 topic_id: 30319 reply_id: 121510[/import]

Have you guys checked out this thread
http://developer.coronalabs.com/forum/2012/07/30/help-jumping

I actually subscribed to it, unfortunately the OP didn’t post his final solution [import]uid: 98393 topic_id: 30319 reply_id: 121511[/import]

it worked!

Thank you very much budershank !!!
[import]uid: 95474 topic_id: 30319 reply_id: 121549[/import]

Thanks to all of you for suggesting! Here’s the solution I came up with is an extension of budershank’s input:

[lua]local function guyGroundCollision(self, event)
if self.groundCount == nil then
self.groundCount = 0
self.groundTimer = nil
end
if event.other.ground == true then
– count number of collisions
if event.phase == “began” then
self.groundCount = self.groundCount + 1
elseif event.phase == “ended” then
self.groundCount = self.groundCount - 1
end

– reset onGround timer
if self.groundTimer ~= nil then
timer.cancel(self.groundTimer)
self.groundTimer = nil
end

– if false, use a small delay to prevent impulse false detection
– due to slight bouncing
if self.groundCount == 0 then
self.groundTimer = timer.performWithDelay(200,
function()
self.onGround = self.groundCount > 0
self.groundTimer = nil
end)
– if true, set it asap
else
self.onGround = self.groundCount > 0
end
end
end

– further down the code
guy.collision = guyGroundCollision
guy:addEventListener(“collision”, guy)[/lua]

What’s different is basically that this one has a short time buffer (200ms) every time the onGround property is about to get set to false. The buffer is super helpful since it keeps the movement responsiveness high especially when the characters gets nudged or bounces a little.

I didn’t put in a sensor body around the character anymore since I got lazy and when I tested this code out it worked nicely.

The only drawback is I have to set the .ground property for all the ground static bodies. I guess this can’t be helped :slight_smile: [import]uid: 144908 topic_id: 30319 reply_id: 121598[/import]

Regarding the “wrapper” solution I stated, I just read this in the docs:

The “collision” event includes phases for “began” and “ended”, which signify the moments of initial contact and broken contact

Haven’t tested though, but this means that the wrapper solution is viable. Although this means that there’ll additional IDs to assign since now I’ll have to add a “ground” id to all the horizontal or slightly tilted ground bodies that I have so that I can check for collision events.

Anyone with a better solution still? [import]uid: 144908 topic_id: 30319 reply_id: 121484[/import]

The most common solution is essentially what you described. For some reason people in Corona seem to actually use the player body itself as the sensor but in past projects where I used box2d in it’s native environment i would add a sensor to the bottom of the character that stuck out a tiny bit. From ther eis just a simple switch of onGround = true/false.

Btw, the reason it stuck out of the bottom is that way I didn’t have to assign special IDs to ground objects. Depending on your mechanics you could do the same.
[import]uid: 147305 topic_id: 30319 reply_id: 121485[/import]

I’m having a problem a bit worse than this:

-the player can only jump if he is on the ground, as well as to your problem.
but the error occurs when the player is on two objects at the same time and when I decide to move to one side of the event “ended” is triggered but the “Began” is not, then the physics of the game registers the player is in the air and he is unable to jump.

can someone help me?
thank you, [import]uid: 95474 topic_id: 30319 reply_id: 121507[/import]

Well, in theory instead of using a boolean checker you could do a counter. so the logic would be as follows(and forgive any syntax errors)

[lua]function onGroundCollission(event)
if event.phase == “began” then
onGround = onGround +1
elseif event.phase == “ended” then
onGround = onGround -1
end

end[/lua]

then when you jump just check if onGround == 0 [import]uid: 147305 topic_id: 30319 reply_id: 121510[/import]

Have you guys checked out this thread
http://developer.coronalabs.com/forum/2012/07/30/help-jumping

I actually subscribed to it, unfortunately the OP didn’t post his final solution [import]uid: 98393 topic_id: 30319 reply_id: 121511[/import]

it worked!

Thank you very much budershank !!!
[import]uid: 95474 topic_id: 30319 reply_id: 121549[/import]

Thanks to all of you for suggesting! Here’s the solution I came up with is an extension of budershank’s input:

[lua]local function guyGroundCollision(self, event)
if self.groundCount == nil then
self.groundCount = 0
self.groundTimer = nil
end
if event.other.ground == true then
– count number of collisions
if event.phase == “began” then
self.groundCount = self.groundCount + 1
elseif event.phase == “ended” then
self.groundCount = self.groundCount - 1
end

– reset onGround timer
if self.groundTimer ~= nil then
timer.cancel(self.groundTimer)
self.groundTimer = nil
end

– if false, use a small delay to prevent impulse false detection
– due to slight bouncing
if self.groundCount == 0 then
self.groundTimer = timer.performWithDelay(200,
function()
self.onGround = self.groundCount > 0
self.groundTimer = nil
end)
– if true, set it asap
else
self.onGround = self.groundCount > 0
end
end
end

– further down the code
guy.collision = guyGroundCollision
guy:addEventListener(“collision”, guy)[/lua]

What’s different is basically that this one has a short time buffer (200ms) every time the onGround property is about to get set to false. The buffer is super helpful since it keeps the movement responsiveness high especially when the characters gets nudged or bounces a little.

I didn’t put in a sensor body around the character anymore since I got lazy and when I tested this code out it worked nicely.

The only drawback is I have to set the .ground property for all the ground static bodies. I guess this can’t be helped :slight_smile: [import]uid: 144908 topic_id: 30319 reply_id: 121598[/import]