2
votes

so I've got a problem I really don't understand. After trying to figure out what's wrong I decided to record a video and ask here.

In the video, keep an eye on the True/False boolean in the left corner. This is my variable canJump's value. In the beginning, the value goes between true and false just by moving the "Player" left and right. This also occurs when the player goes up and down slopes.

The map/collision layer is created with Tiled.

My TiledObject class:

public class TiledObjectUtil {

public static float PPM = 32;

public static void parseTiledObjectLayer(World world, MapObjects objects) {
    for(MapObject object : objects) {
        Shape shape;

        if(object instanceof PolylineMapObject) {
            shape = createPolyLine((PolylineMapObject) object);

        } else {
            continue;
        }

        Body body;
        BodyDef bdef = new BodyDef();
        bdef.type = BodyDef.BodyType.StaticBody;
        body = world.createBody(bdef);
        body.createFixture(shape, 1.0f);

        shape.dispose();
    }
}

private static ChainShape createPolyLine(PolylineMapObject polyline) {
    float[] vertices = polyline.getPolyline().getTransformedVertices();
    Vector2[] worldVertices = new Vector2[vertices.length / 2];

    for(int i = 0; i<worldVertices.length; i++) {
        worldVertices[i] = new Vector2(vertices[i * 2] / PPM, vertices[i*2+1] / PPM);
    }
    ChainShape cs = new ChainShape();
    cs.createChain(worldVertices);
    return cs;
}}

And my Player class:

public class Player {

private BodyDef def = new BodyDef();
public Body playerBody;


private float speed = 10;

public Player() {

}

public void update() {

    if (InputUtil.moveLeft)  {
        playerBody.setLinearVelocity(-speed, playerBody.getLinearVelocity().y);
    }
    if (InputUtil.moveRight)  {
        playerBody.setLinearVelocity(speed, playerBody.getLinearVelocity().y);
    }

    if (!InputUtil.moveLeft && !InputUtil.moveRight) {

        playerBody.setLinearVelocity(0, playerBody.getLinearVelocity().y);
    }
}

public void jump() {
    if (ContactUtil.canJump) {
        playerBody.applyLinearImpulse(0, 80, 0, 0, true);

    }

}


public Body createPlayer(World world) {
    def.type = BodyDef.BodyType.DynamicBody;
    def.position.set(20, 20);
    def.fixedRotation = true;

    playerBody = world.createBody(def);

    PolygonShape shape = new PolygonShape();
    shape.setAsBox(2f / 2, 2f / 2);

    FixtureDef playerFixture = new FixtureDef();
    playerFixture.density = 1f;
    playerFixture.shape = shape;
    playerFixture.restitution = 0f;
    playerFixture.friction = 1f;
    playerBody.createFixture(playerFixture);

    shape.setAsBox(2f / 2, 1f / 2, new Vector2(0, 0 - 1), 0);
    playerFixture.shape = shape;
    playerFixture.isSensor = true;
    playerBody.createFixture(playerFixture).setUserData("player");

    shape.dispose();

    return playerBody;
} }

And lastly, my ContactListener:

public class ContactUtil implements ContactListener {

public static boolean canJump;


public ContactUtil() {

}

@Override
public void beginContact(Contact contact) {

    Fixture fixtureA = contact.getFixtureA();
    Fixture fixtureB = contact.getFixtureB();

    System.out.println(fixtureA.getUserData() + ", " + fixtureB.getUserData());

    if (fixtureA.getUserData() == "player" && fixtureB.getUserData() == null) {
        canJump = true;
    }
    if (fixtureA.getUserData() == null && fixtureB.getUserData() == "player") {
        canJump = true;
    }

}

@Override
public void endContact(Contact contact) {

    Fixture fixtureA = contact.getFixtureA();
    Fixture fixtureB = contact.getFixtureB();

    System.out.println(fixtureA.getUserData() + ", " + fixtureB.getUserData());

    if (fixtureA.getUserData() == "player" && fixtureB.getUserData() == null) {
        canJump = false;
    }
    if (fixtureA.getUserData() == null && fixtureB.getUserData() == "player") {
        canJump = false;
    }

}

Also, my player jumps a little everytime it stops in a slope. I know it's due to this line: playerBody.setLinearVelocity(0, playerBody.getLinearVelocity().y);

If anyone knows a better way to handle the movement, it would be very appreciated.

1

1 Answers

1
votes

I seem to recall having this exact issue in the past! I think the issue is with your logic in beginContact and endContact.

I'll break out my crude MS Paint skills to explain the issue. Imagine the following situation: your player is in the air, falling towards a platform below. As you would expect, canJump is false: enter image description here

The player falls and lands, and beginContact with platform one is called. This sets canJump to true. Again, this is as we expect: enter image description here

Now the player moves right, until they come into contact with platform two. beginContact with platform two is called. Yet again, canJump is set to true. enter image description here

And here's the problem. At the last step, the player was still in contact with platform one. But now they're not, so endContact with platform one is called. canJump is now set to false. This explains the unexpected behaviour you're getting. enter image description here

The solution is pretty simple. You need to maintain a list of contacts that the bottom of the player is touching. There's a guide to do exactly that in this iforce2d article: http://www.iforce2d.net/b2dtut/jumpability

Good luck!