I am looking for a conceptual solution to my problem. It's a simple platformer-alike game where player can move horizontally during free-fall.

Consider those two cases:

In the first case, from game experience point of view, the player should land on top of the box; and in the other case he hit the left edge, hence the player should fall down.

However, from my code point of view ("real behaviour"), both those collision detection cases are identical. I am not sure how to separate them.

In both cases the vertical velocity is positive (falling down) and the user is moving with some fixed positive horizontal velocity. (moving right)

From a collision-standpoint the two cases are identical, I think. How can I tell whether I should put the player on top of it or let him fall?

  • 17
    \$\begingroup\$ It sounds like you're detecting collisions by checking for intersections at discrete times (i.e., once per frame). This is a fine naïve approach, but suffers from lots of problems, including tunnelling. Consider looking into other approaches, like swept collision detection, that properly simulate time-continuous processes rather than just their discretizations! \$\endgroup\$ – wchargin Nov 25 '19 at 0:45
  • 5
    \$\begingroup\$ A simple way would be to compare the aspect ratio of the velocity vector and the collision overlap. If the aspect ratio of the overlap is higher, you reached the top; otherwise, you hit from the side. Note that this is not perfectly precise (it essentially assumes the velocity didn't change during the interpolation between two frames, which isn't true), but it's simple and usually looks fair :) Alternatively, you can just rewind the physics simulation to find the exact point of contact, which gives you a precise answer. \$\endgroup\$ – Luaan Nov 25 '19 at 9:46

There are three common ways games sort this out:

  1. Minimum Separation Vector

    Upon a collision, compute the shortest movement that pushes the bodies apart into a non-intersecting position.

    If I've crept just 0.1 units into the platform's left side, but my feet are 0.2 units below its top surface, then the shortest movement to resolve the penetration is to shift me 0.1 units directly left.

    If the penetration from each face is the same, you can pick one arbitrarily.

  2. Rewind to Moment of First Intersection

    Knowing the penetration depth of the object and its incident velocity, you can compute the moment in its travel this frame that it first contacted the obstacle, and back-track it along its velocity to the moment of contact. This will generally be a diagonal movement, rather than favouring the horizontal or vertical axis exclusively.

    That rewound position will sit either wholly to the left, or wholly above, or exactly corner-to-corner in the most extreme case.

  3. "Love the Player"

    For the particular case of the player controller, we might want to use player experience rather than geometry to guide us.

    As you say, these two situations look almost identical to the player - if in one case they fall and one they land, based on tiny fractional differences in the numbers, then the mechanic is liable to appear inconsistent, capricious, and frustrating to the player.

    Instead, we can choose to make a charitable assumption that honours what the player was trying to do: as long as they haven't sunk too far below the top of the platform (maybe use their falling speed times half a frame as a threshold), correct them to the top of the platform as though they landed on it, even if a perfectly continuous simulation would have said otherwise.

    This will only fudge the player's position by a half a frame's movement, tops, so it generally won't be very noticeable if you choose a suitable threshold, and a landing sprite/animation/dust cloud/sound effect can help sell the landing and mask the fudge factor.

    If you want to increase this tolerance range further, you can add a ledge grab state to trigger on near-misses like this and save the player from a fall when they were "close enough" for your game's target experience, even if raw physics disagrees.

  • 6
    \$\begingroup\$ Option 3 just pushes the problem back further, leading to an ever-expanding invisible ledge. Option 2 is the best in my experience. \$\endgroup\$ – user76284 Nov 25 '19 at 3:59
  • 5
    \$\begingroup\$ @user76284 I'm not sure I understand how option 3 creates an "ever-expanding invisible ledge". Can you expand on the problem? \$\endgroup\$ – Caleb Miller Nov 25 '19 at 6:53
  • 9
    \$\begingroup\$ @CalebMiller "as long as they haven't sunk too far below the top" effectively reduces the height of the hitbox by a certain amount ("maybe use their falling speed times half a frame"). This new, reduced hitbox still has a corner one can hit, repeating the problem. It should maybe be listed as an additional hint, not as a solution to the problem. \$\endgroup\$ – WhiteMaple Nov 25 '19 at 9:29
  • 5
    \$\begingroup\$ @WhiteMaple it doesn't reduce the height of the hitbox, it just introduces a simple threshold value exclusive for the Y axis. No matter how far the X-Position overlaps, if Y is smaller than the threshold the player gets teleported on top, otherwise he will always get moved to the side. \$\endgroup\$ – Falco Nov 25 '19 at 12:12
  • 19
    \$\begingroup\$ WhiteMaple is right that there is still a point where a player at x,y falls, and a player at point x,y+0.0001 lands. There will always be such a cut-off point (unless you add a ledge grab state between the two extremes). What approach #3 does is move that transition point away from the place where, visually, it looks plausible that I should have landed, where a player might be very legitimately frustrated that they fell instead. This way, the player who is closest to landing but still falls is visibly not close to the rendered corner, so it's more obvious that "oops, that was my fault" ;) \$\endgroup\$ – DMGregory Nov 25 '19 at 13:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.