Hey all, I am using Slick2D atm just to explore it and so far, its pretty awesome. After a couple of tutorials… I am making a 2D TopDown Zombie Shooter and whilst atm, all is okay. I really dislike how I have done collision Detection. I create a 2D Boolean array and set it to true in the following way:
- Grab the Tiled (.tmx) map properties and check the blocked property of the tile. If its a blocked, tile. Return true else false
After this, I then do my collision detection between the player and the map, my player can rotate 360 degrees and move forward. Here is where I am having odd glitches, my player collides with the tiles correctly however if the user tries to rotate and keep moving forward, eventually the player will move through the tile.
A simple example:
o = tile, x = player
o
x //player moves upwards and collides with the tile
player then continues to rotate, upon 180 degree rotation the player glitches and moves through the block.
Any ideas on how to fix my collision detection? I am rather stuck with this.
Here is my code:
SimpleGame.java
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/jungle.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 {
boolean collision = false;
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) //1 is a buffer space
|| 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 {
collision = true;
}
}
if (collision == false) {
if (input.isKeyDown(Input.KEY_LEFT)) {
player.rotate(-0.2f * delta);
}
if (input.isKeyDown(Input.KEY_RIGHT)) {
player.rotate(0.2f * delta);
}
}
if (collision == true) {
//reverse the player
newX = 0;
newX -= speed * Math.sin(Math.toRadians(rotation));
newY = 0;
newY += speed * Math.cos(Math.toRadians(rotation));
playerY += newY;
playerX += newX;
}
}
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
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