0
\$\begingroup\$

I've been working on a Line2D based collision system:

public void update() {
    for (int i = 0; i < lines.size(); i++) {
        line = lines.get(i);
        if (line.line.intersects(player.hitbox))
            playerCollision();
    }
}

public void playerCollision() {
    if (Math.abs(line.angle) <= 45) {
        player.jumped = false;

        if (player.speedY > 0)
            player.speedY = 0;

        // inefficient?
        while (line.line.intersects(player.hitbox)) {
            player.centerY--;
            player.hitbox.setRect(player.centerX - player.WIDTH / 2, player.centerY - player.HEIGHT / 2,
                    player.WIDTH, player.HEIGHT);
        }

        player.centerY++;
        player.hitbox.setRect(player.centerX - player.WIDTH / 2, player.centerY - player.HEIGHT / 2, player.WIDTH,
                player.HEIGHT);

    } else if (line.angle > 0) {
        while (line.line.intersects(player.hitbox)) {
            player.centerX--;
            player.hitbox.setRect(player.centerX - player.WIDTH / 2, player.centerY - player.HEIGHT / 2,
                    player.WIDTH, player.HEIGHT);
        }

    } else if (line.angle < 0) {
        while (line.line.intersects(player.hitbox)) {
            player.centerX++;
            player.hitbox.setRect(player.centerX - player.WIDTH / 2, player.centerY - player.HEIGHT / 2,
                    player.WIDTH, player.HEIGHT);
        }
    }
}

Overall, it works pretty well. There is, however, one problem I haven't been able to fix. When the player collides on a place where a wall and the ground meet, both the ground and wall collisions are executed, however, only one should be.

The result is that the player is abruptly pushed off a cliff when approaching one, or that the player is abruptly moved up when jumping alongside a cliff.

When approaching a cliff:

After one step to the right:

I've been able to come up with a method to determine which collision should be executed:

if (player.hitbox.getMaxY() - player.speedY > line.line.getY1())
    // execute wall collision only
if (player.hitbox.getMaxY() - player.speedY <= line.line.getY1())
    // execute ground collision only

However, I haven't been able to come up with how to implement this. I can't check all lines for collision first and execute the collision later, as the collision is executed per line. Does anyone know how I can fix my collision system?

P.S. I'm a beginning game developer, so my collision detection system might be bad or inefficient. If there's a better way to do this, that doesn't have the same problems, I'd love to hear about it. Please keep in mind, though, that I'm trying to keep the code simple and efficient.

\$\endgroup\$
3
  • \$\begingroup\$ Instead of executing the collision per line, you could try saving the collision information - whether it is with a wall or with the ground, and how the player would move in response. When another collision is found, decide whether or not it replaces the saved one by using your method. After the loop, apply the collision response (if any collision was found). \$\endgroup\$
    – Victor T.
    May 2, 2017 at 18:03
  • \$\begingroup\$ I've already gone over this, but it wouldn't work since I use this: while (line.line.intersects(player.hitbox)), meaning that the only way for me to determine the distance the player needs to be moved is by constantly updating it's position. I can't wait till the end. As I said in the last lines of my question, if my system is bad or inefficient, please tell me how to do it differently. Could you give me an example on how to work this out, and submit that as an answer? Anyways, thanks for replying. I guess it would be better for me to move on to libGDX or another engine. \$\endgroup\$ May 2, 2017 at 18:34
  • \$\begingroup\$ I see, you want to intentionally move the player after each intersection. That wasn't clear to me from your original question. I'll write up an answer with a possible solution. \$\endgroup\$
    – Victor T.
    May 2, 2017 at 19:29

1 Answer 1

1
\$\begingroup\$

I think the key to fixing this situation without writing all new collision code is to identify the "cliff" situation while inside playerCollision().

The first step would be to determine which line endpoints are part of a cliff edge - either the wall or the ground at the top of it. This is a pretty simple pre-processing step. As you load or create your level data, search the list of lines for wall and ground segments that make cliff edges. Store with each Line2D whether or not each of its ends is part of a cliff.

The second step is inside playerCollision(), where you need to detect that the player is touching a cliff endpoint. This is also pretty simple, just test whether each line endpoint is part of a cliff, and if it is test whether the player collision shape contains that endpoint. If both are true, then the player is on a cliff edge.

Finally, use your method to decide whether the current segment should push the player or not.

Note 1: I would test my solution by manually adding the cliff information to a simple level. Once the rest of the code works, I'd switch to automating the marking-line-ends-as-cliffs process.

Note 2: Your collision code seems fine, especially for small levels like your example image. If levels get larger and the collision code becomes too slow, you can look for ways to optimize the outer loop and still keep playerCollision() as it is.

\$\endgroup\$

You must log in to answer this question.

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