Collision detection not very good?

So - after a lot of hair pulling and gnashing of teeth - i’ve come to the conclusion that the physics implementation in Corona is not very strong…

I am trying to get a character / sprite to move (I don’t want to make this reference, but it’s the best way to describe it) like a doodle-jump character. Collision between the character and platform works probably 70% of the time. I’ve tried manipulating the “setPosition” and “SetVelocity” iteration methods and it doesn’t seem to improve accuracy. Basically the sprite will occasionally vanish (with the x/y coords being reported as NaN), or it will pass through the top of the object. The object is not moving that fast…

Do I seriously have to roll my own collision system to get this to work??

This is a somewhat hacked together example of the problem. The cube can be moved by selecting the character and dragging down. Please note - this isn’t very robust yet.

[lua]-- Setup the game
– Turn off the status bar
display.setStatusBar(display.HiddenStatusBar);

– Setup graphics
local physics = require(“physics”);
physics.start();
physics.setDrawMode(“hybrid”);
physics.setGravity(0, 9.8);
physics.setPositionIterations(16);
physics.setVelocityIterations(6);

– Setup the background group
local gameGroup = display.newGroup();
gameGroup.x = 0;

– Ground object
local ground = display.newRect(450, 0, 320, 5);
ground.x = 160;
ground.y = 440;
ground.objectType = “ground”
groundPhysics = { -160, 0, 160, 0, 160, 10, -160, 10 };
physics.addBody(ground, “static”, { density=1.0, friction=0.0, bounce=0.0, shape=groundPhysics });
gameGroup:insert(ground);

– Setup a sprite for the player
playerInstance = display.newRect(100, 400, 32, 32);
playerInstance.x = 100;
playerInstance.y = 400;
playerInstance.rotation = 0;
gameGroup:insert(playerInstance);
physics.addBody(playerInstance, “dynamic”, { density=0.25, friction=0.15, bounce=0.0 });
playerInstance.isFixedRotation = true;
playerInstance.objectType = “player”;

– Setup some vars on the playerInstance
playerInstance.isFalling = false;
playerInstance.xVel = 0;
playerInstance.yVel = 0;

– Test jump platform
local jumpPlatform = display.newRect(80, 200, 100, 50);
jumpPlatform.objectType = “jumpPlatform”
gameGroup:insert(jumpPlatform);
physics.addBody(jumpPlatform, “static”, { friction=0.3, isSensor=true} );
– The main loop
function onEnterFrame(event)

– Move the game world to keep the player onscreen
if playerInstance.y <= 200 then
gameGroup.y = -playerInstance.y + 200;
end
end

– When the player makes a tap event
function onTouch(event)

local t = event.target;
local phase = event.phase;

if phase == “began” then
– Make the object the topmost object
local parent = t.parent;
parent:insert(t);
display:getCurrentStage():setFocus(t);

– Prevents spurious messages being sent
t.isFocus = true;

– Store the initial position
t.x0 = event.x - t.x;
t.y0 = event.y - t.y;

elseif t.isFocus == true then

if phase == “moved” then

elseif phase == “ended” or phase == “cancelled” then

local xForce = (-1 * (event.x - playerInstance.x)) * 2.75;
local yForce = (-1 * (event.y - playerInstance.y)) * 2.75;

playerInstance:applyForce( 0, yForce, playerInstance.x, playerInstance.y);

display.getCurrentStage():setFocus(nil);
t.isFocus = false;

end

end

end

local function onCollision(self, event)

local other = event.other.objectType;

xVel, yVel = playerInstance:getLinearVelocity();

–debugTextField1.text = "collision between: " … self.objectType … " and " … other;

if event.phase == “began” then

if (yVel > 0) then
event.other.isSensor = false;
end

elseif event.phase == “ended” then
event.other.isSensor = true;
end

end

– Setup the event listeners
playerInstance.collision = onCollision;
playerInstance:addEventListener(“collision”, playerInstance);
Runtime:addEventListener(“enterFrame”, onEnterFrame);
playerInstance:addEventListener(“touch”, onTouch);[/lua] [import]uid: 41286 topic_id: 17232 reply_id: 317232[/import]

How often are you getting this result? I just tried about 40 times and couldn’t replicate it unless I moused down and out of the simulator which obviously isn’t an issue on device.

Were you getting it 70% with this code, or in your actual project? [import]uid: 52491 topic_id: 17232 reply_id: 65029[/import]

This code - on the simulator. Very frequently. Just got it first time I tried, with that exact code. I just selected the square, dragged down (within the simulator window) and released. The square went just above the platform, landed on the platform - and vanished. I’m assuming it didn’t actually vanish, but was ejected very quickly (linear velocity goes through the roof when this happens). [import]uid: 41286 topic_id: 17232 reply_id: 65031[/import]

Just tried this on the device - and I get the same behavior. [import]uid: 41286 topic_id: 17232 reply_id: 65033[/import]

try to set fps to 60, for a similar problem it worked for me. [import]uid: 44010 topic_id: 17232 reply_id: 65036[/import]

Thanks for the suggestion - it appears to improve matters. But definitely doesn’t solve it. I still get the “player” disappearing occasionally. There is something truly whacky going on.
[import]uid: 41286 topic_id: 17232 reply_id: 65062[/import]

I just saw a colleagues game (didn’t even know he used Corona until today!) - who has the same problem. Every now and again the character will shoot off into space and the coords are reported as NaN… is this a known issue that people are just working around or something? [import]uid: 41286 topic_id: 17232 reply_id: 65190[/import]

I see that you’re changing isSensor in a collision handler, which means collisions are already happening.
From what I’ve read, any changes done to an object while Box2d is calculating collisions can cause unpredictable results.

My guess is that changing isSensor should be done in a preCollision event handler instead.
[import]uid: 70847 topic_id: 17232 reply_id: 65197[/import]

Oooh - that’s a good point… I’ll try some things out. [import]uid: 41286 topic_id: 17232 reply_id: 65199[/import]

So here is the thing. I think the last poster was correct… but actually implementing it is another thing.

So does a preCollision event fire if the object is set to “isSensor=true”? The behavior I am seeing is not. If that is the case, then how do you actually implement one way collisions in this manner?

I.e. it’s fine when the object passes through the first way - start off as a static object, detect the preCollision, switch the object to a sensor. Object passes through… then BAM. Can’t detect it’s going to collide again on the way down because preCollision doesn’t fire. Am I missing something here?

(oh and postCollision events don’t seem to fire once an object has been switched to a sensor). [import]uid: 41286 topic_id: 17232 reply_id: 65321[/import]

What about this?
In the preCollision event you can calculate the speed of the object passing through the platform. You know the thickness of the platform. With these two values you can get the time it will take to pass through the platform. Set the platform to sensor, set up a performWithDelay to set the platform sensor back to false.

If you have multiple players that can pass through the platform at the same time, maybe it would be better to set the player to sensor instead. [import]uid: 70847 topic_id: 17232 reply_id: 65343[/import]

Ehh… Not a bad idea, but too many edge cases that I can think of that will make that flaky.

Changing player to sensor may work…

But seriously, the Corona documentation suggests using the preCollision events in this manner - surely I’m missing something here? [import]uid: 41286 topic_id: 17232 reply_id: 65376[/import]

Okay, I haven’t made a platform game yet, so I was just spawning ideas for how I would accomplish this.

This time I took a closer look at your code and what you’re trying to achieve is possible if you implement a collision and a preCollision event handler as follows:

[lua]local function onPreCollision(self, event)
local player = self;
local collidedWith = event.other;

if (collidedWith.objectType == “ground”) then
return true; – we don’t care about the ground
end

–check to see if player is above the platform
if (player.y < collidedWith.y) then
return true; – player should remain on platform
end

print(“preCollision”);

–set player to sensor while travelling through object
player.isSensor = true;
end

local function onCollision(self, event)
local player = self;
local collidedWith = event.other;
local phase = event.phase;

if (collidedWith.objectType == “ground”) then
return true; – we don’t care about the ground
end

if (phase == “began”) then
print(collidedWith.objectType…" began")

elseif (phase == “ended”) then
print(collidedWith.objectType…" end")
player.isSensor = false;
end
end

– Setup the event listeners
playerInstance.collision = onCollision;
playerInstance.preCollision = onPreCollision;
playerInstance:addEventListener(“collision”, playerInstance);
playerInstance:addEventListener(“preCollision”, playerInstance);[/lua]

This should get you want you want.

Another thing I noticed is that you use applyForce for the jump motion. I think you should be using applyLinearImpulse instead. applyForce is meant to be used for forces that are applied during a length of time. applyLinearImpulse is used for momentary “kicks” in a certain direction.

EDIT:
You have to make sure you remove the isSensor declaration from the platform in your original code for this to work :slight_smile: [import]uid: 70847 topic_id: 17232 reply_id: 65382[/import]

You were spot on - that has rectified the unreliability and allows me to raise the right events. Thank you very much for spending the time. :slight_smile:
[import]uid: 41286 topic_id: 17232 reply_id: 65568[/import]

another thing in physics, you can set your character as isBullet=true, this will do the math calculations faster for it, so it could also resolve the problem. [import]uid: 44010 topic_id: 17232 reply_id: 65622[/import]