patternjavaMinor
Java Slick 2D Collision Detection Methodology
Viewed 0 times
collisionjavamethodologydetectionslick
Problem
I have a collision detection for terrain setup (non-terrain is handled with a quadtree I implemented apart from this) and I was wondering how others have done it compared to mine and what I could learn from people's critiques here.
My map is stored in a 2D array of "GameSquare"s
The Player
My player class gets track of the player in pixels. The speed and X,Y positions are all in exact pixels. I divide those measures by the width or height of the tile graphics I use and truncate it; in order to find the corresponding array cell that the player is in. In order to do collision detection I wrote a method that takes a cardinal direction, the players current collision box, and a distance which is usually their speed.
The cardinal direction is an enum that has methods for the adjX and adjY.
For example; East would return adjX() = 1 and adjY() = 0 whereas
SouthEast would return adjX() = 1 and adjY() = 1 <-- Positive because this is the movement in a 2D array.
The Collision Check
```
public int canMove(Direction dir, Rectangle collisionBox, int distance) {
int goodToGo = 0;
// Check each 'step' of our attempted movement from 1 - distance(ie speed)
for(int i = 1; i cornerPoints = new HashSet();
cornerPoints.add( new Vector2f(collisionBox.getMinX()+dir.adjX()i, collisionBox.getMinY()+dir.adjY()i) );
cornerPoints.add( new Vector2f(collisionBox.getMaxX()+dir.adjX()i, collisionBox.getMinY()+dir.adjY()i) );
cornerPoints.add( new Vector2f(collisionBox.getMinX()+dir.adjX()i, collisionBox.getMaxY()+dir.adjY()i) );
cornerPoints.add( new Vector2f(collisionBox.getMaxX()+dir.adjX()*i, collisi
My map is stored in a 2D array of "GameSquare"s
public class GameSquare extends GameObject {
private char tileLetter = 'W';
private int row, col;
}
public boolean isPassable() {
return tileLetter != 'W';
}- The row and col are the index in the 2D array.
- The char corresponds to the graphic, my render checks the char and draws a Wall(W), Floor(O), or Door(X)
The Player
My player class gets track of the player in pixels. The speed and X,Y positions are all in exact pixels. I divide those measures by the width or height of the tile graphics I use and truncate it; in order to find the corresponding array cell that the player is in. In order to do collision detection I wrote a method that takes a cardinal direction, the players current collision box, and a distance which is usually their speed.
The cardinal direction is an enum that has methods for the adjX and adjY.
For example; East would return adjX() = 1 and adjY() = 0 whereas
SouthEast would return adjX() = 1 and adjY() = 1 <-- Positive because this is the movement in a 2D array.
The Collision Check
```
public int canMove(Direction dir, Rectangle collisionBox, int distance) {
int goodToGo = 0;
// Check each 'step' of our attempted movement from 1 - distance(ie speed)
for(int i = 1; i cornerPoints = new HashSet();
cornerPoints.add( new Vector2f(collisionBox.getMinX()+dir.adjX()i, collisionBox.getMinY()+dir.adjY()i) );
cornerPoints.add( new Vector2f(collisionBox.getMaxX()+dir.adjX()i, collisionBox.getMinY()+dir.adjY()i) );
cornerPoints.add( new Vector2f(collisionBox.getMinX()+dir.adjX()i, collisionBox.getMaxY()+dir.adjY()i) );
cornerPoints.add( new Vector2f(collisionBox.getMaxX()+dir.adjX()*i, collisi
Solution
That really looks quite slow.
First of, don't use HashSet in these situations. A HashSet is a HashMap with "blind" values - like using the key as a value, too. They guarantee uniqueness and fast access (as everyone knows!), but for a set of four objects in a tight and often called loop, you are really hurting performance - the creation overhead is far too high and lookup speed isn't that great.
Next, you should probably change your usage pattern. Don't store distances in
It looks like you never use fractional values and always integers - even in your floats - so you probaly think you can get away with them. They are used as painting positions for the tiles anyway.
Still, even for pixel-precise games, as soon as you change to float, everything starts to get smoother. Because you get sub-pixel movements, which wouldn't be possible for
Next, you can use a vastly better collision check method.
Vastly better in most cases as least. If every object is rectangular, you'd be ok with your way - but there's a lot of conditionals involved and you end up with code like this:
The other (better) way:
Use bounding circles. For each object, you store
Then... pythagoras (aa + bb = c*c)! For entities
It's like calculating the distance -
I really love how it's totally independend of the orientation of the objects towards each other - you don't need any
Here, I call the method
First of, don't use HashSet in these situations. A HashSet is a HashMap with "blind" values - like using the key as a value, too. They guarantee uniqueness and fast access (as everyone knows!), but for a set of four objects in a tight and often called loop, you are really hurting performance - the creation overhead is far too high and lookup speed isn't that great.
Next, you should probably change your usage pattern. Don't store distances in
int if everything else is float. Same goes the other way around - the conversion between int and float is not free and you should avoid it in code that has to be really fast.It looks like you never use fractional values and always integers - even in your floats - so you probaly think you can get away with them. They are used as painting positions for the tiles anyway.
Still, even for pixel-precise games, as soon as you change to float, everything starts to get smoother. Because you get sub-pixel movements, which wouldn't be possible for
int. You can't display them, but they still affect percieved smoothness. I'd go for float, always keep positional data as floats and only convert to int when I need it to draw stuff.Next, you can use a vastly better collision check method.
Vastly better in most cases as least. If every object is rectangular, you'd be ok with your way - but there's a lot of conditionals involved and you end up with code like this:
if (!(a.maxY b.maxY)) blocked = true; ....The other (better) way:
Use bounding circles. For each object, you store
float x, y, boundsRadius; - the center and the distance to the entities point with the largest distance from the center.Then... pythagoras (aa + bb = c*c)! For entities
e1 and e2:public static boolean maybeCollision(Entity e1, Entity e2) {
float xDistance = e1.x - e2.x,
yDistance = e1.y - e2.y,
rSum = e1.boundsRadius + e2.boundsRadius
;
return xDistance * xDistance + yDistance * yDistance < rSum * rSum;
}It's like calculating the distance -
Math.sqrt(dx dx + dy dy) - but we don't use square root (which is on the slow side) and square the sum of the radius instead. You can use float or int - it doesn't matter as long as there is no overflow. Just change the local variables.I really love how it's totally independend of the orientation of the objects towards each other - you don't need any
ifs, the square of the distance automatically makes the result positive and it just works.Here, I call the method
maybeCollision (and that's a bad name, as it's not a verb. Still fits!) because you still have to check per pixel - it sees anything inside of the circle as a collision. But it's lightning fast and you have to check with bounding boxes, too (as long as you don't write Tetris or a round-based Minecraft)...Code Snippets
public static boolean maybeCollision(Entity e1, Entity e2) {
float xDistance = e1.x - e2.x,
yDistance = e1.y - e2.y,
rSum = e1.boundsRadius + e2.boundsRadius
;
return xDistance * xDistance + yDistance * yDistance < rSum * rSum;
}Context
StackExchange Code Review Q#8414, answer score: 2
Revisions (0)
No revisions yet.