1
\$\begingroup\$

I'm trying to learn some things about collision detection, and I've never known how to cleanly detect this for multiple objects in multiple classes without just copy pasting a lot of code.

I now came up with all collisionable/solid things implementing a class and then using Java streams to return whether one or more things in the list of solid things is colliding with the thing you are checking. Is this the way to do this? Or is this really performance heavy or unconventional? I also didn't know a good way of getting the collisionable objects to the player/other creatures, so I simply used a getter, but that means that player class will get access to all the things in the arraylist and I don't know if this is save.

And are there good resources on collision detection for Java(8)?

And if this were to be for a partially grid based game with unwalkable positions in the grid, but also moving objects that cannot be walked through, would something like this still be the correct way to go about it?

Relevant code:

GameScreen.java

class GameScreen implements Screen {
private Stage stage;
private Game game;
private Player player;
private ArrayList<Solid> solids;

GameScreen(Game game) {
    this.game = game;
    stage = new Stage(new ScreenViewport());

    solids = new ArrayList<>();
    solids.add(new Wall(500, 0, 200, 200));
    solids.add(new Rock(200, 600, 100, 100));

    player = new Player(this);
    stage.addActor(player);
}

public ArrayList<Solid> getSolids() {
    return solids;
}
}

Rock.java (Wall.java is exactly the same but with Rock replaced by Wall)

class Rock extends Group implements Solid {

private int x;
private int y;
private int width;
private int height;

Rock(int x, int y, int width, int height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
}

@Override
public boolean collidesWith(float oX, float oY, float oWidth, float oHeight) {
    return (oX + oWidth > x && oX < x + width)
            && (oY + oHeight > y && oY < y + height);
}
}

Player.java

class Player extends Group {
private final float STEP = 10f;
private GameScreen gameScreen;
private int playerHeight = 130;

Player(Screen gameScreen) {
    setBounds(300, 300, playerHeight / 6.5f * 5, playerHeight);
    this.gameScreen = (GameScreen) gameScreen;
}

private boolean collidesWith(ArrayList<Solid> solids, float x, float y) {
    return solids.stream().filter(o -> o.collidesWith(x, y, getWidth(), getHeight())).findAny().isPresent();
}

@Override
public void act(float delta) {
    super.act(delta);

    if(Gdx.input.isKeyPressed(Input.Keys.W)) {
        if (!collidesWith(gameScreen.getSolids(), getX(), getY() + STEP)) {
            this.setPosition(getX(), getY() + STEP);
        }
    }
    if(Gdx.input.isKeyPressed(Input.Keys.S)) {
        if (!collidesWith(gameScreen.getSolids(), getX(), getY() - STEP))
            this.setPosition(getX(), getY() - STEP);
    }
    if(Gdx.input.isKeyPressed(Input.Keys.A)) {
        if (!collidesWith(gameScreen.getSolids(), getX() - STEP, getY()))
            this.setPosition(getX() - STEP, getY());
    }
    if(Gdx.input.isKeyPressed(Input.Keys.D)) {
        if (!collidesWith(gameScreen.getSolids(), getX() + STEP, getY()))
            this.setPosition(getX() + STEP, getY());
    }
}

}

Solid.java

interface Solid {
  boolean collidesWith(float oX, float oY, float oWidth, float oHeight);
}
\$\endgroup\$
1
  • \$\begingroup\$ I believe this would be a better fit for CodeReview SE. \$\endgroup\$
    – Charanor
    Apr 19, 2017 at 5:45

1 Answer 1

2
\$\begingroup\$

And are there good resources on collision detection for Java(8)?

TL;DR- when dealing with collisions, you're almost always better off using 3rd party engines like Box2D which is compatible with libGDX and have had the heck optimized out of them.

I now came up with all collisionable/solid things implementing a class and then using Java streams to return whether one or more things in the list of solid things is colliding with the thing you are checking. Is this the way to do this? Or is this really performance heavy or unconventional?

It's not great because you're going to check the entire list. I'm not sure what the implementation of Stream is (I looked in the source code; not a lot of luck), but fundamentally if you need to check to see if each object has collided with another, it's a O(N^2) operation.

Physics engines get around this by saying that some entities (like your rocks) are "Static"- they can collide, but never move. If they never move, we don't need to check for collisions between them. Thus, if we can say we have N moving objects and M static objects, we get O(NM + NN), which often gets simplified to O(N*M) if you have more static objects than moving ones.

Then you can simplify things further by dividing your world into "chunks"- if there aren't any moving parts in a chunk, you don't need to do collision checking against any of the static objects in that chunk.

Like I said, Box2D already does all of this for you (and more), so I would recommend you invest the time needed to learn it.

I also didn't know a good way of getting the collisionable objects to the player/other creatures, so I simply used a getter, but that means that player class will get access to all the things in the arraylist and I don't know if this is save.

If you go the physics engine route this will become a moot point, but theoretically if you didn't, I would say if you're worried about the Player class messing with the list somehow, you could invert the dependency as shown in the pseudocode below:

interface Collidable {
  default boolean collidesWith(Collidable o) {
    return (o.getX() + o.getWidth() > getX() && o.getX() < getX() + getWidth())
        && (o.getY() + o.getHeight() > getY() && o.getY() < getY() + getHeight());
  }
  float getWidth();
  float getHeight();
  float getX();
  float getY();
}
class Player implements Collidable { ... }
class Rock implements Collidable { ... }
// ...
ArrayList<Collidable> rocks = ...
Player player = ...
// ...
public void render(delta) {
    // Without Streams
    for(Collidable rock : rocks)
      if(rock.collidesWith(player)
        // Your code here
    // With Streams
    rocks.filter(r -> r.collidesWith(player)).forEach(r -> // Your code here);
}

Maybe not the cleanest, but illustrates how you could make absolutely sure that rocks and players only know how to collide with each other.

\$\endgroup\$

You must log in to answer this question.

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