Saturday, December 5, 2015

c# - 2D Platformer AABB collision problems


http://dl.dropbox.com/u/3724424/Programming/Gifs/game6.gif


I have a problem with AABB collision resolution.




I resolve AABB intersection by resolving the X axis first, then the Y axis. This is done to prevent this bug: http://i.stack.imgur.com/NLg4j.png




The current method works fine when an object moves into the player and the player has to be pushed horizontally. As you can see in the .gif, the horizontal spikes push the player correctly.




When the vertical spikes move into the player, however, the X axis is still resolved first. This makes "using the spikes as a lift" impossible.



When the player moves into the vertical spikes (affected by gravity, falls into them), he's pushed on the Y axis, because there was no overlap on the X axis to begin with.




Something I tried was the method described in the first answer of this link: 2D rectangular object collision detection


However the spikes and moving objects move by having their position changed, not velocity, and I don't calculate their next predicted position until their Update() method is called. Needless to say this solution didn't work either. :(




I need to solve AABB collision in a way that both of the cases described above work as intended.


This is my current collision source code: http://pastebin.com/MiCi3nA1


I'd be really grateful if someone could look into this, since this bug has been present in the engine all the way back from the beginning, and I've been struggling to find a good solution, without any success. This is seriously making me spend nights looking at the collision code and preventing me from getting to the "fun part" and coding the game logic :(




I tried implementing the same collision system as in the XNA AppHub platformer demo (by copy-pasting most of the stuff). However the "jumping" bug occurs in my game, while it doesn't occur in the AppHub demo. [ jumping bug: http://i.stack.imgur.com/NLg4j.png ]



To jump I check if the player is "onGround", then add -5 to Velocity.Y.


Since the player's Velocity.X is higher than Velocity.Y (refer to the fourth panel in the diagram), onGround is set to true when it shouldn't be, and thus lets the player jump in mid-air.


I believe this doesn't happen in the AppHub demo because the player's Velocity.X will never be higher than Velocity.Y, but I may be mistaken.


I solved this before by resolving on the X axis first, then on the Y axis. But that screws up the collision with the spikes as I stated above.



Answer



OK, I figured out why the XNA AppHub platformer demo doesn't have the "jumping" bug: the demo tests the collision tiles from top to bottom. When up against a "wall" the player may be overlapping multiple tiles. The resolution order is important because resolving one collision may also resolve other collisions (but in a different direction). The onGround property is only set when the collision is resolved by pushing the player up on the y-axis. This resolution will not occur if the previous resolutions pushed the player down and/or horizontally.


I was able to reproduce the "jumping" bug in the XNA demo by changing this line:


for (int y = topTile; y <= bottomTile; ++y)

to this:



for (int y = bottomTile; y >= topTile; --y)

(I also tweaked some of the physics-related constants, but this should not matter.)


Perhaps sorting bodiesToCheck on the y-axis before resolving the the collisions in your game will fix the "jumping" bug. I suggest resolving the collision on the "shallow" axis of penetration, as the XNA demo does and Trevor suggests. Also note the XNA demo player is twice as tall as the collide-able tiles, making the multiple collision case more likely.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...