开发者

box2d: How to prevent a character from being able to jump in midair?

So I've been playing around with box2d on iOS (thought this question isn't really limited to iOS), and I've got a small demo going where a player character jumps when the user taps the screen via ApplyLinearImpulse.

For the most part, it works. Except if I tap while the player is in midair, ApplyLinearImpulse is called again, and the player "jumps" again, but in midair. Now, it'd be pretty neat to do that in real life, but you can't, and I'd prefer my player not be able to do that either.

So I've been trying to come up with a decent way to prevent jumping while a player is already jumping, and I'm not sure where to go from here - my best thought is to try something like this:

  • Attach a body that is just a sensor to the bottom of my player with a fixed joint.
  • When the player jumps, disable jumping until the aforementioned sensor detects a collision (i.e., when the player "lands" on something, which may or may not be the ground).
  • Once a collision has been detected, re-enable jumping.

I'm not sure how much I like this idea, for one, I'm not sure if attaching a sensor开发者_运维技巧 to the bottom of my player is the "smartest" way of detecting collisions on the bottom of my player sprite. I haven't given this a try yet, I wanted to instead get SO's input on it and see if anyone could provide a better alternative. Any ideas?

Edit: mafutrct had good a suggestion: try some sort of downward hit test, using a ray straight downward from my player's body, and see if that ray intersects another object within a short distance (i.e., right below my player) - is that type of ray-casting hit test possible with box2d?


My vote goes to the 'sensor at the player's feet' method. You could make the sensor fixture an edge if you really want a ray type check, but generally I think it's more useful to be able to specify an area, especially if your character will be able to stand on more than one thing at once, and if you are interested in keeping an up-to-date list of what's being stood on. If you are creating the player's body in a graphical editor then it will also be more intuitive to place and look at the sensor instead of having it all running programmatically with raycasts. I believe the foot sensor method is also less cpu cost overall, and less work than implementing raycast checks yourself. I wrote a detailed tutorial about it, I hope it's useful: http://www.iforce2d.net/b2dtut/jumpability


You need to check if the feet touch the ground at the moment of jumping.

Depending on your model, measure the distance between your feet and the ground. Either apply a vector from the model center and check for a vertex hit, or (more sophisticated) check for the first vertical hit of a polygon describing your feet area with the ground.

Simply checking for any collision fails since you may jump against a wall, which still counts as midair (unless you play Unreal Tournament, in which it is a wall jump).

I am not familiar with your software though, so this is just an idea - would be great if it is actually helpful. Good luck :)


The way to solve this problem is to create a class that implements ContactListener and then when you create the World, you .setContactListener() to your class. Now your class will be notified of all collisions. From there you create a flag for if the player is in contact with any ground objects.

Assume that you've set the shapes' userData to the relevant GameObjects (shapeDef.userData = this; in GameObject constructor). Assume you have an enum that defines the types of GameObject in your game, then you have a contact listener along these lines:

import org.jbox2d.dynamics.ContactListener;
import org.jbox2d.dynamics.contacts.ContactPoint;
import org.jbox2d.dynamics.contacts.ContactResult;
import android.util.Log;

import GameObject.GAME_OBJECT_TYPE;

public class CollisionChecker implements ContactListener {

    private boolean groundCollision = false;

    @Override
    public void add(ContactPoint arg0) {
        // Called when a contact point is added. This includes the geometry and the forces.
    }

    @Override
    public void persist(ContactPoint arg0) {
        // Called when a contact point persists. This includes the geometry and the forces.
    }

    @Override
    public void remove(ContactPoint arg0) {
        // Called when a contact point is removed. This includes the last computed geometry and forces.
    }

    @Override
    public void result(ContactResult arg0) {
        // This is called once the collision has been resolved 

        GAME_OBJECT_TYPE a = ((GameObject) arg0.shape1.getUserData()).getType();
        GAME_OBJECT_TYPE b = ((GameObject) arg0.shape2.getUserData()).getType();
        //check for ground
        groundCollision = check(a, b, GAME_OBJECT_TYPE.ground);
    }

    private boolean check(GAME_OBJECT_TYPE a, GAME_OBJECT_TYPE b, GAME_OBJECT_TYPE test){
        if (a == test && b == GAME_OBJECT_TYPE.player){
            return true;
        }
        else if (a == GAME_OBJECT_TYPE.player && b == test){
            return true;
        }
        return false;
    }

    public boolean isGroundCollision(){
        return groundCollision;
    }

    public void step() {
        /*
        if (groundCollision)
            Log.d("COLLSN", "We have a collision with ground");*/
        //after logic, reset the state variables
        reset();
    }

    private void reset(){
        groundCollision = false;
    }
}

Now you create a jump boolean in your player. When he jumps, set it to true. He can no longer jump when the flag is true. Test every frame for a collision with ground with 'collisionChecker.isGroundCollision()'. When a collision occurs, set player.jump = false again.

There is a problem here in that if you have vertical ground, the player will be able to walljump. If the player hits his head against ground he will also be able to jump more. To solve these issues you check the 'arg0.position' to find out where the point of contact was in relation to the player object. If the contact is not underneath the player, then he's hitting his head or hitting a wall.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜