I am writing an algorithm for collision detection in slick2d + java that I will eventually use in a platformer game that I will be making. How the algorithm works is it detects how much the player is overlapping a rectangle and then moves the player out of the rectangle by that overlap. The problem is, the algorithm has quite a few issues that I can't figure out how to fix. First, sometimes the player move too far out of the rectangle so it looks it is bouncing off it. Secondly sometimes the player is able to move a small but noticeable amount inside of the rectangle. Lastly, if I increase the velocity sometimes the player can pass all the way through the rectangle. This is quite a vague question but I really need some help figuring out what is wrong. Any ideas at all would be greatly appreciated. The source code should compile without any problems if you have Slick2D installed.
Algorithm:
public void Collision(Polygon player, Polygon poly, Vector2f translation){
Vector2f magnitude = new Vector2f();
//Find the vectre of each object
Vector2f p1Centre = new Vector2f(player.getX() + (player.getWidth()/2), player.getY() + (player.getHeight()/2));
Vector2f p2Centre = new Vector2f(poly.getX() + (poly.getWidth()/2), poly.getY() + (poly.getHeight()/2));
//Calculate the distance between the two
Vector2f distance = new Vector2f(p1Centre);
distance.sub(p2Centre);
//Get the absolute distance
Vector2f absDistance = new Vector2f(distance.x<0 ? -distance.x : distance.x, distance.y<0 ? -distance.y : distance.y);
//Get the combined half widths and heights of each object
Vector2f halvedBounds = new Vector2f((player.getWidth() + poly.getWidth())/2.0f, (player.getHeight() + poly.getHeight())/2.0f);
//If the absolute distance is less thate the halved widths heights then there is a collision
if((absDistance.x < halvedBounds.x) && (absDistance.y < halvedBounds.y)){
//Set the magnitude vector to the halved bounds minus the absolute distance
magnitude.x = halvedBounds.x - absDistance.x;
magnitude.y = halvedBounds.y - absDistance.y;
//Only react to the lesser overlap;
if(magnitude.x < magnitude.y){
magnitude.x = (distance.x > 0) ? magnitude.x : -magnitude.x;
magnitude.y = 0;
}
else{
magnitude.y = (distance.y > 0) ? magnitude.y : -magnitude.y;
magnitude.x = 0;
}
//Debug
System.out.println(magnitude.x+" "+magnitude.y);
System.out.println(translation.x+" "+translation.y+"\n");
//Add the magnitude to the player position
position.add(magnitude);
}
}
Full Source:
import java.util.ArrayList;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Polygon;
import org.newdawn.slick.geom.Vector2f;
public class TestCode extends BasicGame {
private Vector2f position = new Vector2f(300, 300);
private ArrayList<Polygon> solids;
private Polygon player;
public TestCode(String title) {
super(title);
}
public static void main(String[] args) throws SlickException{
AppGameContainer game = new AppGameContainer(new TestCode("test"));
game.setDisplayMode(800, 600, false);
game.start();
}
@Override
public void render(GameContainer gc, Graphics g) throws SlickException {
if(gc.isPaused()){
g.setColor(Color.red);
g.drawString("Paused", 90, 10);
}else{
g.setColor(Color.green);
g.drawString("Playing", 90, 10);
}
g.setColor(Color.red);
for(Polygon p : solids)
g.fill(p);
g.setColor(Color.cyan);
g.fill(player);
}
@Override
public void init(GameContainer gc) throws SlickException {
gc.setVSync(true);
solids = new ArrayList<Polygon>();
player = new Polygon(new float[]{
50, 50, // upper left point
70, 50, // upper right
70, 90, // lower right
50, 90 // lower left
});
for(int i=0, x=200, y=200; i<10; i++, x+=40){
solids.add(new Polygon(new float[]{
x, y, // upper left point
x+40, y, // upper right
x+40, y+40, // lower right
x, y+40 // lower left
}));
}
}
@Override
public void update(GameContainer gc, int delta) throws SlickException {
Input input = gc. getInput();
Vector2f translation = new Vector2f(0, 0);
if(input.isKeyDown(Input.KEY_UP))
translation.y = -1f;
if(input.isKeyDown(Input.KEY_DOWN))
translation.y = 1f;
if(input.isKeyDown(Input.KEY_LEFT))
translation.x = -1f;
if(input.isKeyDown(Input.KEY_RIGHT))
translation.x = 1f;
translation.normalise();
translation.x*=2;
translation.y*=2;
position.add(translation);
for(Polygon p : solids)
Collision(player, p, translation);
player.setLocation(position);
}
public void Collision(Polygon player, Polygon poly, Vector2f translation){
Vector2f magnitude = new Vector2f();
//Find the vectre of each object
Vector2f p1Centre = new Vector2f(player.getX() + (player.getWidth()/2), player.getY() + (player.getHeight()/2));
Vector2f p2Centre = new Vector2f(poly.getX() + (poly.getWidth()/2), poly.getY() + (poly.getHeight()/2));
//Calculate the distance between the two
Vector2f distance = new Vector2f(p1Centre);
distance.sub(p2Centre);
//Get the absolute distance
Vector2f absDistance = new Vector2f(distance.x<0 ? -distance.x : distance.x, distance.y<0 ? -distance.y : distance.y);
//Get the combined half widths and heights of each object
Vector2f halvedBounds = new Vector2f((player.getWidth() + poly.getWidth())/2.0f, (player.getHeight() + poly.getHeight())/2.0f);
//If the absolute distance is less thate the halved widths heights then there is a collision
if((absDistance.x < halvedBounds.x) && (absDistance.y < halvedBounds.y)){
//Set the magnitude vector to the halved bounds minus the absolute distance
magnitude.x = halvedBounds.x - absDistance.x;
magnitude.y = halvedBounds.y - absDistance.y;
//Only react to the lesser overlap;
if(magnitude.x < magnitude.y){
magnitude.x = (distance.x > 0) ? magnitude.x : -magnitude.x;
magnitude.y = 0;
}
else{
magnitude.y = (distance.y > 0) ? magnitude.y : -magnitude.y;
magnitude.x = 0;
}
//Debug
System.out.println(magnitude.x+" "+magnitude.y);
System.out.println(translation.x+" "+translation.y+"\n");
//Add the magnitude to the player position
position.add(magnitude);
}
}
}