16
\$\begingroup\$

I'm building a simple Plattformer Jump n' Run Style game. I do not use tiles - instead i've geometrical shapes for my level entities (and the player is one too). I finished my collision detection code and everything works fine so far.

Next, I wanted to implement jumping. Just checking if the player hits the appropriate key and add some upwards velocity. Works fine. But it works even if the player is in-air, which is not what I want. ;-)

So, I have to check if the player stands on something. My first idea was to check if there was a collision in the last frame and mark the player as "able to jump", but this would even trigger if the player hits a wall in-air. As my math skills aren't that good, i ask for help - even hints would do how to implement this.

Thanks!

\$\endgroup\$

3 Answers 3

14
\$\begingroup\$

Two options spring to mind:

  • First thought is to tag the geometry with an ID and then check to see if the collision is with geometry tagged as floor. This offers the most control of jumpable surfaces but at a cost in level creation time.
  • Second is to check the normal of the collision, if it's pointing up then allow jump. Or within some margin of up, depends on if you have slanting floors. This is flexible and doesn't require any tagging, but if you have slanted floors and walls you might get some jumping where you don't want it.
\$\endgroup\$
4
  • \$\begingroup\$ The normal check seems like a great idea to me. Thanks for posting that. \$\endgroup\$ Commented Oct 26, 2010 at 18:29
  • \$\begingroup\$ +1 for normal checking. THis ensures that a floor can seamlessly transition into a wall without causing any issues. \$\endgroup\$
    – Soviut
    Commented Oct 26, 2010 at 22:04
  • 1
    \$\begingroup\$ +1 Definitely check the collision-normal. Having different types of world-geometry is fine and allows for a more flexible level design, but it doesn't solve you primary issue. A "wall" might also be of type "ground", depending if you're running into it or standing on it. That's where the collision-normal will help. \$\endgroup\$
    – bummzack
    Commented Oct 27, 2010 at 7:21
  • \$\begingroup\$ I find the normal solution very nice and would solve my issue. Even the other high-rated answer is nice and such - but this answers my question better. Thank your wkerslake! \$\endgroup\$
    – Malax
    Commented Oct 27, 2010 at 7:32
8
\$\begingroup\$

You surely should implement some kind of surface type. Think of it, how will you manage if you can climb up a ladder if you can't know if your character just collided a wall or a ladder? You could simply use OOP to manage a type hierarchy using heritage, but I would suggest you to use "categories" implemented using an enumerated type:

Here is the idea: A "Collisions" enumeration has a flag for each category. For example:

namespace Collisions
{
    enum Type
    {
        None   = 0,
        Floor  = 1 << 0,
        Ladder = 1 << 1,
        Enemy  = 1 << 2,
        ... // And whatever else you need.

        // Then, you can construct named groups of flags.
        Player = Floor | Ladder | Enemy
    };
}

With this method, you will be able to test if the player juste collided anything that you should manage, so your engine can call a "Collided" Method of the entity :

void Player::Collided( Collisions::Type group )
{
   if ( group & Collisions::Ladder )
   {
      // Manage Ladder Collision
   }
   if ( group & Collisions::Floor )
   {
      // Manage Floor Collision
   }
   if ( group & Collisions::Enemy )
   {
      // Manage Enemy Collision
   }
}

The method use bitwise flags and the bitwise "Or" operator to assure each group have a different value, based on category's binary value. This method works fine and is easily scalable so you can create customs collision groups. Each Entity (Player, Enemy, etc.) in your game has some bits called a "filter", which are used to determine what it can collide with. Your collision code should check to see if bits match and react accordingly, with some code that might look like:

void PhysicEngine::OnCollision(...)
{
    mPhysics.AddContact( body1, body1.GetFilter(), body2, body2.GetFilter() );
}
\$\endgroup\$
6
  • \$\begingroup\$ Is there a reason to use const ints instead of an enum? In the degenerate case of a single flag or named group the enumerated type is more readable, and you get additional type-checking abilities in most compilers. \$\endgroup\$
    – user744
    Commented Oct 26, 2010 at 19:34
  • \$\begingroup\$ Well yes, the enum can't (I think) use binary operators to create new custom groups. The reason why I made theses const is that I use a Config class with public static members for readability reasons, keeping security of config parameters. \$\endgroup\$ Commented Oct 26, 2010 at 19:41
  • \$\begingroup\$ It can. Enumeration "rvalues" can be bitwise operations on other constant values (I think it can be any constant expression actually, but I'm too lazy to check the standard). Non-static values are achieved in exactly the same way as your example, but are more accurately typed. I have no idea what you mean by "security", but exactly the same syntax without the overhead associated with a class can be achieved with a namespace. \$\endgroup\$
    – user744
    Commented Oct 26, 2010 at 19:44
  • \$\begingroup\$ I updated your code examples to use an enumerated type. \$\endgroup\$
    – user744
    Commented Oct 26, 2010 at 19:55
  • \$\begingroup\$ Maybe it does make thinks easier to read. I approuve the changes you made, the method I used was correct but I had not adjusted the code for the explaination enouph, thank you for the corrections. For the namespace, you are quite right, but this is not true for all languages (Scripting languages for exemple). The "Security" is that my config options are static so they can't be modified but using an enum prevent this as well. \$\endgroup\$ Commented Oct 26, 2010 at 22:56
1
\$\begingroup\$

If you consider your character's foot as alway under the character by a certain distance, and if you're not moving away from the surface then your character is on the ground.

In rough pseudo-code:

bool isOnGround(Character& chr)
{
   // down vector is opposite from your characters current up vector.
   // if you want to support wall jumps, animate the vecToFoot as appropriate.
   vec vecToFoot = -chr.up * chr.footDistanceFromCentre;

// if feet are moving away from any surface, can't be on the ground if (dot(chr.velocity, down) < 0.0f) return false;

// generate line from character centre to the foot position vec linePos0 = chr.position; vec linePos1 = chr.position + vecToFoot;

// test line against world. If it returns a hit surface, we're on the ground if (testLineAgainstWorld(line0, line1, &surface) return true;

return false; }

\$\endgroup\$
6
  • \$\begingroup\$ This won't work if you want to add features such as double jumps, or wall jumps I think ? \$\endgroup\$ Commented Oct 26, 2010 at 19:32
  • \$\begingroup\$ I don't believe the OP mentioned double jumps or wall jumps, did he? \$\endgroup\$
    – jpaver
    Commented Oct 26, 2010 at 20:10
  • \$\begingroup\$ I've revised code to show how to abstract the vector to your foot from character centre. If you animate this, and feet end up sideways and hit a wall, you'll be able to support wall jump. \$\endgroup\$
    – jpaver
    Commented Oct 26, 2010 at 20:21
  • \$\begingroup\$ This is basically a less flexible way of checking the collision normal. (Less flexible because you can trivially distinguish a wall jump from a floor jump from a ceiling jump just by checking the normal's direction.) \$\endgroup\$
    – user744
    Commented Oct 26, 2010 at 20:31
  • 2
    \$\begingroup\$ Never call a variable char, not even in pseudo-code. \$\endgroup\$
    – user2566
    Commented Oct 27, 2010 at 13:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .