Hey all,
I am very new to Slick2D and have so far really been enjoying it however I have found that there is little documentation on colllisions with Slick2D. Anyways, after reading a couple of tutorials and messing around for a bit, I began on my first game but I am now stuck with how to get my collision Detection to work.
I believe that the way I have done the collision detection takes into account all 4 sides of the player and both directions but I am now stuck. The player stops moving when he hits a blocked obstacle hence I do believe the collision detection is working as it detects that the player hits the obstacle but it does not allow the player to move any further. Any ideas?
I first grab the Tile Data from the Tiled (.tmx) map:
Code:
// build a collision map based on tile properties in the TileD map
blocked = new boolean[map.getWidth()][map.getHeight()];
for (int xAxis = 0; xAxis < map.getWidth(); xAxis++) {
for (int yAxis = 0; yAxis < map.getHeight(); yAxis++) {
int tileID = map.getTileId(xAxis, yAxis, 0);
String value = map.getTileProperty(tileID, "blocked", "false");
if (value.equals("true")) {
blocked[xAxis][yAxis] = true;
}
}
}
}
checks if the new position is blocked against the generated 2d boolean array
Code:
private boolean isBlocked(float x, float y) {
int xBlock = (int) x / SIZE;
int yBlock = (int) y / SIZE;
return blocked[xBlock][yBlock];
}
And this is my movement + collision code:
Code:
@Override
public void update(GameContainer container, int delta)
throws SlickException {
Input input = container.getInput();
float rotation = player.getRotation();
float speed = 0.1f * delta;
float newX = 0;
float newY = 0;
newX += speed * Math.sin(Math.toRadians(rotation));
newY -= speed * Math.cos(Math.toRadians(rotation));
if (input.isKeyDown(Input.KEY_UP)) {
// Check all directions
if (!(isBlocked(playerX, playerY - newY)
|| isBlocked(playerX + SIZE - 1, playerY - newY)
|| isBlocked(playerX, playerY + SIZE + newY)
|| isBlocked(playerX + SIZE - 1, playerY + SIZE + newY)
|| isBlocked(playerX - newX, playerY)
|| isBlocked(playerX - newX, playerY + SIZE - 1)
|| isBlocked(playerX + SIZE + newX, playerY) || isBlocked(
playerX + SIZE + newX, playerY + SIZE - 1))) {
// The lower the delta the slowest the sprite will animate.
blockedStr = "Free";
playerX += newX;
playerY += newY;
} else {
blockedStr = "Blocked"; // if collision, change string (for
// debug purposes)
}
}
if (input.isKeyDown(Input.KEY_LEFT)) {
player.rotate(-0.2f * delta);
}
if (input.isKeyDown(Input.KEY_RIGHT)) {
player.rotate(0.2f * delta);
}
}
Here is a full listingSimpleGame.java
Code:
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.tiled.TiledMap;
public class SimpleGame extends BasicGame {
Image land = null;
Image player = null;
float playerX = 400f;
float playerY = 300f;
float scale = 1.0f;
private TiledMap map;
private Camera cam;
private String blockedStr = "free";
int currentBullet = 0;
/**
* The collision map indicating which tiles block movement - generated based
* on tile properties
*/
private boolean[][] blocked;
private static final int SIZE = 32;
public SimpleGame() {
super("Window");
}
// overriden methods are the 3 methods that we will add code later to, they
// make up our game loop
@Override
public void init(GameContainer gc) throws SlickException {
map = new TiledMap("res/map1.tmx");
player = new Image("res/player.png"); // Note that this is Slick2D's
// Image class not AWT's Image
// class.
cam = new Camera(gc, map);
// build a collision map based on tile properties in the TileD map
blocked = new boolean[map.getWidth()][map.getHeight()];
for (int xAxis = 0; xAxis < map.getWidth(); xAxis++) {
for (int yAxis = 0; yAxis < map.getHeight(); yAxis++) {
int tileID = map.getTileId(xAxis, yAxis, 0);
String value = map.getTileProperty(tileID, "blocked", "false");
if (value.equals("true")) {
blocked[xAxis][yAxis] = true;
}
}
}
}
@Override
public void update(GameContainer container, int delta)
throws SlickException {
Input input = container.getInput();
float rotation = player.getRotation();
float speed = 0.1f * delta;
float newX = 0;
float newY = 0;
newX += speed * Math.sin(Math.toRadians(rotation));
newY -= speed * Math.cos(Math.toRadians(rotation));
if (input.isKeyDown(Input.KEY_UP)) {
// Check all directions
if (!(isBlocked(playerX, playerY - newY)
|| isBlocked(playerX + SIZE - 1, playerY - newY)
|| isBlocked(playerX, playerY + SIZE + newY)
|| isBlocked(playerX + SIZE - 1, playerY + SIZE + newY)
|| isBlocked(playerX - newX, playerY)
|| isBlocked(playerX - newX, playerY + SIZE - 1)
|| isBlocked(playerX + SIZE + newX, playerY) || isBlocked(
playerX + SIZE + newX, playerY + SIZE - 1))) {
// The lower the delta the slowest the sprite will animate.
blockedStr = "Free";
playerX += newX;
playerY += newY;
} else {
blockedStr = "Blocked"; // if collision, change string (for
// debug purposes)
}
}
if (input.isKeyDown(Input.KEY_LEFT)) {
player.rotate(-0.2f * delta);
}
if (input.isKeyDown(Input.KEY_RIGHT)) {
player.rotate(0.2f * delta);
}
}
private boolean isBlocked(float x, float y) {
int xBlock = (int) x / SIZE;
int yBlock = (int) y / SIZE;
return blocked[xBlock][yBlock];
}
@Override
public void render(GameContainer gc, Graphics g) throws SlickException {
// after calculating the positions of all entities
cam.centerOn(playerX, playerY);
// in the render()-method
cam.drawMap();
cam.translateGraphics();
// no depth in slick, images are rendered as the code executes so if we
// render the player before the land, it will be underneath (no
// transparency)
player.draw(playerX, playerY);
g.drawString("blocked:" + blockedStr, playerX - 10, playerY - 10);
}
public static void main(String[] args) throws SlickException {
AppGameContainer app = new AppGameContainer(new SimpleGame());
app.setDisplayMode(800, 600, false);
app.start();
}
}
Camera.java
Code:
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.tiled.TiledMap;
//Example Camera Class
public class Camera {
protected TiledMap map;
protected int numTilesX;
protected int numTilesY;
protected int mapHeight;
protected int mapWidth;
protected int tileWidth;
protected int tileHeight;
protected GameContainer gc;
protected float cameraX;
protected float cameraY;
public Camera(GameContainer gc, TiledMap map) {
this.map = map;
this.numTilesX = map.getWidth();
this.numTilesY = map.getHeight();
this.tileWidth = map.getTileWidth();
this.tileHeight = map.getTileHeight();
this.mapHeight = this.numTilesX * this.tileWidth;
this.mapWidth = this.numTilesY * this.tileHeight;
this.gc = gc;
}
public void centerOn(float x, float y) {
//try to set the given position as center of the camera by default
cameraX = x - gc.getWidth() / 2;
cameraY = y - gc.getHeight() / 2;
//if the camera is at the right or left edge lock it to prevent a black bar
if(cameraX < 0) cameraX = 0;
if(cameraX + gc.getWidth() > mapWidth) cameraX = mapWidth - gc.getWidth();
//if the camera is at the top or bottom edge lock it to prevent a black bar
if(cameraY < 0) cameraY = 0;
if(cameraY + gc.getHeight() > mapHeight) cameraY = mapHeight - gc.getHeight();
}
public void centerOn(float x, float y, float height, float width) {
this.centerOn(x + width / 2, y + height / 2);
}
public void centerOn(Shape shape) {
this.centerOn(shape.getCenterX(), shape.getCenterY());
}
public void drawMap() {
this.drawMap(0, 0);
}
public void drawMap(int offsetX, int offsetY) {
//calculate the offset to the next tile (needed by TiledMap.render())
int tileOffsetX = (int) - (cameraX % tileWidth);
int tileOffsetY = (int) - (cameraY % tileHeight);
//calculate the index of the leftmost tile that is being displayed
int tileIndexX = (int) (cameraX / tileWidth);
int tileIndexY = (int) (cameraY / tileHeight);
//finally draw the section of the map on the screen
map.render(
tileOffsetX + offsetX,
tileOffsetY + offsetY,
tileIndexX,
tileIndexY,
(gc.getWidth() - tileOffsetX) / tileWidth + 1,
(gc.getHeight() - tileOffsetY) / tileHeight + 1);
}
/**
* Translates the Graphics-context to the coordinates of the map - now everything
* can be drawn with it's NATURAL coordinates.
*/
public void translateGraphics() {
gc.getGraphics().translate(-cameraX, -cameraY);
}
/**
* Reverses the Graphics-translation of Camera.translatesGraphics().
* Call this before drawing HUD-elements or the like
*/
public void untranslateGraphics() {
gc.getGraphics().translate(cameraX, cameraY);
}
}
Any help is appreciated,
v0rtex