2
\$\begingroup\$

I'm creating platforms in my game where the player can:

1) Jump through them from the bottom and land on top of them

2) While on top of them, drop down through them

I've been able to get #2 working just by disabling the contact using contact->SetEnabled(false) and this has worked fine. I'm mainly having some issues on the best way to handle #1. Here's the basic idea I have, though there's still some things that I need to concretely figure out. (Note: The logic below is very Box2D specific, but should apply to most physics engines):

  • Add 4 sensor fixtures to the 4 sides of the main fixture that extends a small amount past the main fixture
  • When the top, left, or right sensor fixtures make contact with a platform, disable the contact by using contact->SetEnabled(false) and set a boolean flag inside the Sprite object indicating that the Player is passing through the platform
  • When the bottom fixture ends contact with the platform, disable the boolean flag

This would basically make the top, left, and right fixtures act as enablers for the pass through logic and the bottom fixture act as the disabler. So when the player is falling back down from their jump, the bottom fixture will make contact, but won't disable the contact.

This makes sense on paper, but I can think of a few corner-case issues that might arise from this. Before I start plugging down this path, does anyone have any suggestions that might make my plan better or even a completely different way of doing it?

\$\endgroup\$
2
\$\begingroup\$

I'm pretty sure you can deal with this by ignoring the collision if the y-component of the velocity is positive (the player is moving upward).

Alternately you might be able to use the collision filtering flags. One-way floors are given their own contact group, and the player object can set/unset whether or not they collide with this group based on their vertical velocity.

\$\endgroup\$
  • \$\begingroup\$ Your y-component suggestion solves the problem partially, though there's still a few corner cases that you run in to. EX: player falling next to a platform, then move inward to the platform as they pass it, essentially moving into the bottom left/right corner of the platform. There's also some jittery-ness if you jump partially upward into a platform and then fall down. \$\endgroup\$ – SaviorXTanren Jun 19 '16 at 9:14
1
\$\begingroup\$

I was actually able to accomplish this by doing the following (there might be a better way than this, but it worked). This answer below is a bit specific to Box2D, as this was the original question's context. Here are the requirements we want

  • When we make contact with the top of a platform, we want to enable the collision and stop the player
  • If the player is trying to drop down and they are on top of a platform, disable the collision and let them fall
  • The player should only be able to drop down through one platform at a time
  • If the player is jumping upward into a platform, only enable the collision if the player ends up making it higher than the top of the platform

Bullet-list steps:

  • Make a variable called groundedPlatform and a boolean flag called isDroppingDown on the player
  • Add a sensor fixture to the player's bottom
  • Add a sensor fixture to the platform's top
  • In the player's Update method, check if the player is grounded and makes the action to drop down (Down button + Jump button for my game), set isDroppingDown to true
  • In the BeginContact listener, if the two fixture's are the player's bottom sensor fixture and the platform's top sensor fixture AND the player's bottom is < the platform's top, set groundedPlatform equal to that platform
  • In the PreSolve listener, if the other fixture is part of a platform:
    • If isDroppingDown == true, contact->SetEnabled(false)
    • Else if the platform is NOT the same as groundedPlatform
      • If the player's bottom < platform's top, set groundedPlatform equal to this platform
      • Else contact->SetEnabled(false);
  • In the EndContact listener, if the other fixture is part of a platform AND the platform is the same as groundedPlatform, set groundedPlatform equal to null and isDroppingDown = false

Psuedocode:

Platform* groundedPlatform;
bool isDroppingDown;

Update() {
    if (player is grounded && player is pressing (Down key + jump key)) {
         isDroppingDown = true;
    }
}

BeginContact() {
    if ((player's bottom < platform's top) && (player's bottom sensor in contact) && (platform's top sensor is in contact))
        groundedPlatform = platform;
    }
}


PreSolve() {
    if (platform is other contact) {
        if (isDroppingDown) {
            contact->SetEnabled(false);
        }
        else if (groundedPlatform != platform) {
            if (player's bottom < platform's top) {
                groundedPlatform = platform;
            }
            else {
                contact->SetEnabled(false);
            }
        }
    }
}


EndContact() {
    if ((groundedPlatform is in contact))
        groundedPlatform = null;
        isDroppingDown = false;
    }
}
\$\endgroup\$

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.