3
\$\begingroup\$

It took me many days to create a working collision detection for a 2D java platformer. I am finally calculating the corners of my player but the system is still buggy and I hope that we can figure out the problem.

This is a part of my Entity class. First, I calculate the movement in which direction the entity is moving or if it is falling or jumping and determine my dx and dy fields. After that I check the collision by calculating the corners and set dx or dy to zero if something is blocking the entity. Finally, I perform the moving by adding dx and dy to the x and y position:

    public abstract class Entity extends GameObject {

    //constants
    private final float GRAVITY = 0.2F;
    private final float MAX_FALLING_SPEED = 2.5F;
    private final float JUMP_START = -3.5F;

    //movement
    protected float dx;
    protected float dy;
    protected float speed;

    //movement action
    protected boolean right;
    protected boolean left;
    protected boolean falling;
    protected boolean jumping;

    //collision
    protected boolean topLeft;
    protected boolean topRight;
    protected boolean bottomLeft;
    protected boolean bottomRight;
    protected boolean midLeft;
    protected boolean midRight;
    protected boolean hasfallen;

    public Entity(float x, float y, int width, int height, float speed) {
        super(x, y, width, height);
        this.speed = speed;
    }

    public void update() {
        calculateMovement(); //1
        calculateCollisions(); //2
        move(); //3
    }

    private void calculateMovement() {
        if(left) dx = -speed;
        if(right) dx = speed;

        if(falling && !jumping) {
            dy += GRAVITY;
            if(dy > MAX_FALLING_SPEED) dy = MAX_FALLING_SPEED;
        }

        if(jumping && !falling) {
            dy = JUMP_START;
            jumping = false;
            falling = true;
        }
    }

    private void calculateCollisions() {

        float tox = x + dx;
        float toy = y + dy;

        //LEFT AND RIGHT COLLISION
        calculateCorners(tox, y);
        if(dx < 0) {
            if(topLeft || bottomLeft || midLeft) {
                dx = 0;
            }
        }

        if(dx > 0) {
            if(topRight || bottomRight || midRight) {
                dx = 0;
            }
        }

        //BOTTOM AND TOP (FALLING) COLLISION
        calculateCorners(x, toy);
        if(bottomLeft || bottomRight && falling) {
            falling = false;
            dy = 0;
        } 

        if(!bottomLeft && !bottomRight) {
            falling = true;
        }

        //here's the error, he is constantly switching 
        //between false and true for both booleans after
        //the entity hit the ground
        System.out.println(bottomLeft + "   " + bottomRight);
    }

    private void calculateCorners(float x, float y) {
        World world = Playstate.world;
        int leftTile = world.getColTile((int)x);
        int rightTile = world.getColTile((int)x + width - 1);
        int topTile = world.getRowTile((int)y);
        int bottomTile = world.getRowTile((int)y + height - 1);
        int midTile = world.getRowTile((int)y + height / 2);
        topLeft = !world.getBlocks()[topTile][leftTile].getMaterial().isWalkable();
        bottomLeft = !world.getBlocks()[bottomTile][leftTile].getMaterial().isWalkable();
        topRight = !world.getBlocks()[topTile][rightTile].getMaterial().isWalkable();
        bottomRight = !world.getBlocks()[bottomTile][rightTile].getMaterial().isWalkable();
        midLeft = !world.getBlocks()[midTile][leftTile].getMaterial().isWalkable();
        midRight = !world.getBlocks()[midTile][rightTile].getMaterial().isWalkable();
    }


    private void move() {
        x += dx;
        y += dy;
        dx = 0;
    }
}

The left and right collision work fine. The problem occurs when the entity hit the ground and falling stops. For example after falling my console says my entity is at position y: 44.9 but it should be 50 to fit to the tile. Apart from that, as you can see I checked the bottomLeft and bottomRight in my calculateCollisions() method. The console returns constantly true and false so in conclusion my entity is switching between falling and not falling fastly. Finally this causes a bug that you can't jump sometimes because if the entity is falling, jumping is set to false. So the entity will only jump if you hit the right moment when bottomLeft and bottomRight are true.

My Platformer

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

You can check the collision before actually moving the entity. Check for collisions of the entity plus DX and DY, if no collisions happen, then move the entity by adding DX and DY to x and y. If a collision happens, then resolve the collision, which might be something like

if (collidedObject.isType("block")) {
    entity.x = collidedObject.topX
}

This type of check will need to be combined with checks for DX positive or negative and DY positive and negative (so you know to use topX, or botX, or leftY, or rightY)

You also need to be wary of delta movements that might move an object completely past a smaller object.

Frame 1:
x     <-

Frame 2: (dx moves x past arrow, bounding boxes do NOT collide)
       <-     x

If the dx is large, you might need to subdivide the collision checks into multiple checks per frame.

EDIT: you can also resolve your entity movement to whole integers each frame. EDIT 2: here's a good summary of movement in platformers. http://higherorderfun.com/blog/2012/05/20/the-guide-to-implementing-2d-platformers/

\$\endgroup\$
1
\$\begingroup\$

Now it works, I made changes here:

int bottomTile = world.getRowTile((int)y + height - 1);

I removed the -1 at the end.

calculateCorners(tox, y -1);

And I added a -1 to calculate Corners for right and left collision

\$\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.