Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am working on a Pacman clone. I am using Java for the project. I have been having problems with collisions, and allowing the player to pass through objects. Note that the tile size (and player) is 16x16px.

Here is the collision code in the player class.

private void processInput(Input input, int delta)  {
    if (input.isKeyDown(Input.KEY_LEFT) || input.isKeyDown(Input.KEY_RIGHT) ||
        input.isKeyDown(Input.KEY_UP) || input.isKeyDown(Input.KEY_DOWN))  {

        if (input.isKeyDown(Input.KEY_LEFT))  {
            velocity =  new Vector2(-1 * speed * delta, 0);

            rotation = 180f;
        }
        else if (input.isKeyDown(Input.KEY_RIGHT))  {
            velocity = new Vector2(1 * speed * delta, 0);

            rotation = 0f;
        }
        else if (input.isKeyDown(Input.KEY_UP))  {
            velocity = new Vector2(0, -1 * speed * delta);

            rotation = 270f;
        }
        else if (input.isKeyDown(Input.KEY_DOWN))  {
            velocity = new Vector2(0, 1 * speed * delta);

            rotation = 90f;
        }
    }

    processCollision();

    this.position.x += velocity.x;
    this.position.y += velocity.y;
}

private void processCollision()  {
    this.positionTile = new Vector2(position.x, position.y);

    this.positionTile.x -= board.getX();
    this.positionTile.y -= board.getY();

    this.positionTile.x /= 16;
    this.positionTile.y /= 16;

    Vector2 normalisedVelocity = new Vector2(velocity.x, velocity.y);
    normalisedVelocity = velocity.getNormal();

    if (collision(normalisedVelocity))  {
        velocity = Vector2.ZERO;
    }
}

private boolean collision(Vector2 direction)  {
    Vector2 index = new Vector2(positionTile.x + direction.x, positionTile.y + direction.y);

    if (!board.getTiles()[(int)index.y][(int)index.x].collidable)  {
        return true;
    }

    return false;
}

And here is the board class.

public class Board {
    TileSheet sheet;
    TileRenderer renderer;

    Tile[][] tiles;

    Vector2 initPlayerPosition;

    float x = 0;
    float y = 0;

    public float getX()  {
        return x;
    }

    public float getY()  {
        return y;
    }

    public TileSheet getSheet()  {
        return sheet;
    }

    public Tile[][] getTiles()  {
        return tiles;
    }

    public int getWidth()  {
        return tiles[0].length;
    }

    public int getHeight()  {
        return tiles.length;
    }

    public Vector2 getInitPosition()  {
        return initPlayerPosition;
    }

    public Board()  {
    }

    public void loadLevel(String name)  {

        // Temp, the following five lines (including the whitespace) look ugly 
        // TODO: Make the map placing dynamic
        this.x = 800 / 2 - (tiles.length * sheet.getTileWidth()) / 2;
        this.y = 20;

        initPlayerPosition.x += this.x;
        initPlayerPosition.y += this.y;
    }

    public void init(GameContainer gameContainer, StateBasedGame game) throws SlickException  {
        Image sheetImg = new Image("data/textures/tileset.png");
        sheet = new TileSheet(sheetImg, 16, 16, 1.0f);

        renderer = new TileRenderer(this);
    }

    public void render(GameContainer gameContainer, StateBasedGame game, Graphics graphics)  {
        renderer.render(tiles, gameContainer, game, graphics);
    }

...

public class Tile  {
    int sheetX;
    int sheetY;

    bool collidable;

    public Tile(int sheetX, int sheetY)  {
        this.sheetX = sheetX;
        this.sheetY = sheetY;

        // If there is nothing (0,0) allow the player to collide
        if (sheetX == 0 && 
            sheetY == 0)  {
           collidable = true;
        }  else  {
           collidable = false;
        }
    }
}

Any ideas?

share|improve this question
5  
What kind of problems are you experiencing? –  Luis Estrada Feb 27 '12 at 3:12
    
Well, When the pacman guy moves, there is no collision (the player doesn't stop moving in the direction that is specified by pressing an arrow key) in processCollision you can see that the collision function is tested against the velocity, seeing if there is a solid tile in that direction, the problem is, is that I may or may not be indexing the array correctly, or there may be some flaw in my logic, the positionTile object's x and y are subtracted from the board's coordinates on the screen, and then they are turned into coordinates that can be used to index an array (tile sizeX/Y = 16) –  Darestium Feb 27 '12 at 4:40
    
Try debugging (System.out.println()) your direction vector in the collision method, what do you see? I would be expecting either <0, 1> or <1, 0> –  Luis Estrada Feb 27 '12 at 4:47
    
I think you might need to remove the NOT in the if statement of your collision method. If it is collidable you should be returning true, but you NOT it, so it will return false if there is a collision. –  Luis Estrada Feb 27 '12 at 4:57
    
Sadly it didn't change anything. Altough yeah, that is a mistake. –  Darestium Feb 27 '12 at 6:11

2 Answers 2

If you're unsure about whether or not you lookup the array correctly, you can add some print statements (System.out.println("...");) that spits out the index and result of the array lookup.

I suggest you refactor your code to deal with game logic separate from drawing details.

You could represent your board as a 2d integer array with integer value referencing tile type. Then store logical position as integers x and y.

(Look up "Normal form" as it relates to databases to get a sense of how to structure your data efficiently)

Generally you want to use as small amount of data as possible to represent a given concept. Not only for performance reasons, but also to keep code simple and elegant.

For instance, you store sheetX and sheetY for every single tile in the map. Sheet x and y will never be different for two tiles of the same type, so it makes no sense to store it for each tile. Rather have a 1 dimensional array of tile types, then build your game board using references to tile types.

Sheet x and y don't really have to be stored at all if your tilesheet have consistently sized tiles. You can just index the tile and calculate the x and y.

Rectangle getTileGraphicBounds(int index,
        int tileSheetWidth,
        int tileSheetHeight,
        int tileWidth,
        int tileHeight) {

    // TODO: insert tests here to make sure sheet width and hight are multiples of tile width and height

    int horizontalTileCount = tileSheetWidth / tileWidth;

    // Calculate x and y

    // modulus yields the remainder of index divided by horizontalTileCount, which is the x value
    int tileX = index % horizontalTileCount;

    // index divided by horizontalTileCount will yield the integer y value as the remainder gets discarded
    int tileY = index / horizontalTileCount;

    int sheetX = tileX * tileWidth;
    int sheetY = tileY * tileHeight;

    return new Rectangle(sheetX, sheetY, tileWidth, tileHeight);
}

To get nice smooth animations in your graphical presentation of the game you'll need to store position in terms of pixels, not board position. For scalable graphics you need to store subpixel position using floats or doubles.

In the drawing part of your program you could have the position of the character graphic represented as float x and y.

For every iteration of the main loop check if drawPosition is close to logicalPosition (with a suitable threshold, 0.001f for instance). If it is you check for input and perform movement, if it's not, you animate the drawPosition towards the logicalPosition. While animating it makes sense not to check input (no movement can be made until the animation stops).

This will keep the game logic simpler and makes it easier to avoid problems with collition and such.

Example game logic (partial, untested):

// in main class:
Vector2i logicPosition;
Board board;

...

class Vector2i() {
    int x;
    int y;

    Vector2i(int x, int y) {
        this.x = x;
        this.y = y;
    }

    Vector2i() { this(0, 0); }

    Vector2i add(Vector2i other) {
        return new Vector2i(this.x + other.x, this.y + other.y);
    }
}

...

class Board() {
    // 2d array representing level, values referencing tile type.
    int[][] data;

    // A list of all walkable tile types.
    ArrayList<Integer> walkable = new ArrayList<Integer>();

    bool isWalkable(Vector2i position) {
        // data[][] is indexed y, x to make level prototyping easier.
        return walkable.contains(data[position.y][position.x]);
    }
}

...

// Calls move() to perform collition/motion.
void processInput(Input input) {
    Vector2i motion = new Vector2i();

    if (input.isKeyDown(Input.KEY_UP)) motion.y = -1;
    else if (input.isKeyDown(Input.KEY_DOWN)) motion.y = 1;
    else if (input.isKeyDown(Input.KEY_LEFT)) motion.x = -1;
    else if (input.isKeyDown(Input.KEY_RIGHT)) motion.x = 1;

    move(motion);
}

...

// Collition test happens here.
void move(Vector2i motion) {
    Vector2i futurePosition = this.logicPosition.add(motion);

    if (board.isWalkable(futurePosition)) {
        this.logicPosition = futurePosition;
    }
}
share|improve this answer

Ok so i'm currently making a 2D top down view game and the way you created your board i think is similar to mine or maybe not lol.. however! this is how i implement mine.

This is my getTile method.

public Tile[][] getTile(int x, int y) {
    if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) {
    return new VoidTile();
} else {
    return tiles[x][y];
}
}

In my Tile.java class i have a isSolid() method which returns whether the tile is solid or not. However, my tiles extend my Tile.java so they inherit this method and I override it in their constructor. I am not sure if you have other tiles? It is good practice to do it this way though :)

In your case for a pacman clone you could have VoidTile.java, WallTile.java and you could also use the Tile.java class for the fruit that pacman picks up (i.e GrapeTile.java).

In my player.java class i have a checkBlockedDirection(int x, int y) method and it looks like this.

    public void checkBlockedDirection(int x, int y) {
    boolean u = map.getTile(x, y - 1).isSolid();
    boolean d = map.getTile(x, y + 1).isSolid();
    boolean l = map.getTile(x - 1, y).isSolid();
    boolean r = map.getTile(x + 1, y).isSolid();

    if (u) {
        uBlocked = true;
        System.out.println("up tile blocked");
    } else {
        uBlocked = false;
    }
    if (d) {
        dBlocked = true;
        System.out.println("down tile blocked");
    } else {
        dBlocked = false;
    }
    if (l) {
        lBlocked = true;
        System.out.println("left tile blocked");
    } else {
        lBlocked = false;
    }
    if (r) {
        rBlocked = true;
        System.out.println("right tile blocked");
    } else {
        rBlocked = false;
    }
}

Then in my player update method i have this

public void tick() {
    float dx = 0;
    float dy = 0;

    if (input.up.isPressed()) {
        direction = 0;
    } else if (input.down.isPressed()) {
        direction = 2;
    } else if (input.left.isPressed()) {
        direction = 3;
    } else if (input.right.isPressed()) {
        direction = 1;
    } else {
        direction = 4; // standing
    }

    checkBlockedDirection((int)x, (int)y);


    if (input.up.isPressed() && y > 0 && !uBlocked) {
            dy += -speed;
    } else if (input.down.isPressed() && y < map.getHeight() - 1 && !dBlocked) {
            dy += speed;
    } else if (input.left.isPressed() && x > 0 && !lBlocked) {
            dx += -speed;
    } else if (input.right.isPressed() && x < map.getWidth() - 1 && !rBlocked) {
            dx += speed;
    }
    x += dx;
    y += dy;

}

Basically it just checks which direction the player is facing, Then checks if the blocked up, left, right and down are either blocked or not.

If they aren't blocked then it moves the player across one block.

Not sure if this helps or not but it's just my take on this kind of grid collision detection :)

Enjoy

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.