Game Development Stack Exchange is a question and answer site for professional and independent game developers. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have my map stored in 2 arrays:

  • Tiles : Tile names
  • TileTypes : SOLID or BASIC

I want to be able to walk through BASIC Tiles (Like ground tiles). But not the SOLID Tiles (Like wall tiles).

I have tried many ways of doing this and I always fail.

Map Code:

package game;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;


import javax.imageio.ImageIO;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Color;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;



public class Map {
     Random random=new Random();
    public int BushProbability=8;

    public int ticks;
    public int waterType;

    public int MX=384;
    public int MY=284;

    int mapWidth=1000;
    int mapSize;
    public String[][] Tiles = new String[(mapWidth+1)*2][(mapWidth+1)*2];
    public String[][] TileTypes = new String[(mapWidth+1)*2][(mapWidth+1)*2];
    int Tile;

    public Texture grass;
    public Texture stone;
    public Texture water;
    public Texture bush;

    public Texture water1;
    public Texture water2;
    public Texture water3;

    public Texture stone1;


    BufferedImage map = null;

    public void Init()
    {
        try {
            grass = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Tiles/Grass.png"));
            bush = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Tiles/Bush.png"));
            stone = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Tiles/Stone.png"));
            water1 = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Tiles/Water.png"));
            water2 = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Tiles/Water1.png"));
            water3 = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Tiles/Water2.png"));
            stone1 = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Tiles/Stone1.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

     public void LoadMap()
        {
         File file= new File("res/map.png");
        try {
            map = ImageIO.read(file);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        mapWidth=map.getWidth();
        mapSize=mapWidth*mapWidth;

            for(int Y = 0;Y<mapWidth;Y++){
                for(int X = 0;X<mapWidth;X++){

                      // Getting pixel color by position x=100 and y=40 
                      int clr=  map.getRGB(X,Y); 
                      int  red   = (clr & 0x00ff0000) >> 16;
                      int  green = (clr & 0x0000ff00) >> 8;
                      int  blue  =  clr & 0x000000ff;

              if(red==0&&green==0&&blue==0){
                  Tiles[X][Y]="VOID";
                  TileTypes[X][Y]="SOLID";
                  //System.out.println("BLACK "+X+" "+Y);
              }
              if(red==255&&green==0&&blue==0){
                  if(random.nextInt(5)==0){
                      Tiles[X][Y]="SOLIDSTONE1";
                      TileTypes[X][Y]="SOLID";
                  }else{
                  Tiles[X][Y]="SOLIDSTONE2";
                  TileTypes[X][Y]="SOLID";
                  }
                  //System.out.println("RED "+X+" "+Y);
              }
              if(red==0&&green==255&&blue==0){


                  if(random.nextInt(BushProbability)==0){
                      Tiles[X][Y]="BUSH"; 
                      TileTypes[X][Y]="BASIC";
                  }else{
                  Tiles[X][Y]="GRASS";}
                  //System.out.println("GREEN "+X+" "+Y);
              }
              if(red==0&&green==0&&blue==255){
                  Tiles[X][Y]="WATER";
                  TileTypes[X][Y]="SOLID";
                 // System.out.println("BLUE "+X+" "+Y);
              }

            }
            }
            }       




        public void RenderMap()
        { ticks++;
        if(ticks==10){
            waterType++;
            if(waterType==3){waterType=1;}
            ticks=0;
        }
        if(waterType==0){
            water=water1;
        }if(waterType==1){
            water=water2;
        }
        if(waterType==2){
            water=water3;
        }

            for(int X=0;X<mapWidth;X++){
                for(int Y=0;Y<mapWidth;Y++){

                    if(Tiles[X][Y]=="GRASS"){
                        Color.white.bind();
                        grass.bind(); // or GL11.glBind(texture.getTextureID());

                        GL11.glBegin(GL11.GL_QUADS);
                            GL11.glTexCoord2f(0,0);
                            GL11.glVertex2f((MX+(X*32)),(MY+(Y*32)));
                            GL11.glTexCoord2f(1,0);
                            GL11.glVertex2f(grass.getTextureWidth()+(MX+(X*32)),(MY+(Y*32)));
                            GL11.glTexCoord2f(1,1);
                            GL11.glVertex2f(grass.getTextureWidth()+(MX+(X*32)),grass.getTextureHeight()+(MY+(Y*32)));
                            GL11.glTexCoord2f(0,1);
                            GL11.glVertex2f((MX+(X*32)),grass.getTextureHeight()+(MY+(Y*32)));
                        GL11.glEnd();
                    }

                    if(Tiles[X][Y]=="SOLIDSTONE2"){
                        Color.white.bind();
                        stone.bind(); // or GL11.glBind(texture.getTextureID());

                        GL11.glBegin(GL11.GL_QUADS);
                            GL11.glTexCoord2f(0,0);
                            GL11.glVertex2f((MX+(X*32)),(MY+(Y*32)));
                            GL11.glTexCoord2f(1,0);
                            GL11.glVertex2f(stone.getTextureWidth()+(MX+(X*32)),(MY+(Y*32)));
                            GL11.glTexCoord2f(1,1);
                            GL11.glVertex2f(stone.getTextureWidth()+(MX+(X*32)),stone.getTextureHeight()+(MY+(Y*32)));
                            GL11.glTexCoord2f(0,1);
                            GL11.glVertex2f((MX+(X*32)),stone.getTextureHeight()+(MY+(Y*32)));
                        GL11.glEnd();
                    }

                    if(Tiles[X][Y]=="SOLIDSTONE1"){
                        Color.white.bind();
                        stone1.bind(); // or GL11.glBind(texture.getTextureID());

                        GL11.glBegin(GL11.GL_QUADS);
                            GL11.glTexCoord2f(0,0);
                            GL11.glVertex2f((MX+(X*32)),(MY+(Y*32)));
                            GL11.glTexCoord2f(1,0);
                            GL11.glVertex2f(stone1.getTextureWidth()+(MX+(X*32)),(MY+(Y*32)));
                            GL11.glTexCoord2f(1,1);
                            GL11.glVertex2f(stone1.getTextureWidth()+(MX+(X*32)),stone1.getTextureHeight()+(MY+(Y*32)));
                            GL11.glTexCoord2f(0,1);
                            GL11.glVertex2f((MX+(X*32)),stone1.getTextureHeight()+(MY+(Y*32)));
                        GL11.glEnd();
                    }

                    if(Tiles[X][Y]=="WATER"){
                        Color.white.bind();
                        water.bind(); // or GL11.glBind(texture.getTextureID());

                        GL11.glBegin(GL11.GL_QUADS);
                            GL11.glTexCoord2f(0,0);
                            GL11.glVertex2f((MX+(X*32)),(MY+(Y*32)));
                            GL11.glTexCoord2f(1,0);
                            GL11.glVertex2f(water.getTextureWidth()+(MX+(X*32)),(MY+(Y*32)));
                            GL11.glTexCoord2f(1,1);
                            GL11.glVertex2f(water.getTextureWidth()+(MX+(X*32)),water.getTextureHeight()+(MY+(Y*32)));
                            GL11.glTexCoord2f(0,1);
                            GL11.glVertex2f((MX+(X*32)),water.getTextureHeight()+(MY+(Y*32)));
                        GL11.glEnd();
                    }

                    if(Tiles[X][Y]=="BUSH"){
                        Color.white.bind();
                        bush.bind(); // or GL11.glBind(texture.getTextureID());

                        GL11.glBegin(GL11.GL_QUADS);
                            GL11.glTexCoord2f(0,0);
                            GL11.glVertex2f((MX+(X*32)),(MY+(Y*32)));
                            GL11.glTexCoord2f(1,0);
                            GL11.glVertex2f(bush.getTextureWidth()+(MX+(X*32)),(MY+(Y*32)));
                            GL11.glTexCoord2f(1,1);
                            GL11.glVertex2f(bush.getTextureWidth()+(MX+(X*32)),bush.getTextureHeight()+(MY+(Y*32)));
                            GL11.glTexCoord2f(0,1);
                            GL11.glVertex2f((MX+(X*32)),bush.getTextureHeight()+(MY+(Y*32)));
                        GL11.glEnd();
                    }



                }
            }
        }
}

Player code:

package mob;

import java.io.IOException;

import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Color;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

public class Player extends game.Map {

    public int SPEED=10;

    public String Colour="WHITE";

    public int mapWidth;

    int DX = (800/2)-16;
    int DY = (600/2)-16;

    public int X;
    public int Y;

     int RX; int RY;

    boolean UP;
    boolean DOWN;
    boolean LEFT;
    boolean RIGHT;


    private Texture texture;

    public void Init()
    {
        try {
            // load texture from PNG file

            if(Colour == "WHITE"){
                texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Mob/Slime/White.png"));
            }
            if(Colour == "YELLOW"){
                texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Mob/Slime/Yellow.png"));
            }
            if(Colour == "RED"){
                texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Mob/Slime/Red.png"));
            }
            if(Colour == "PURPLE"){
                texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Mob/Slime/Purple.png"));
            }
            if(Colour == "PINK"){
                texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Mob/Slime/Pink.png"));
            }
            if(Colour == "ORRANGE"){
                texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Mob/Slime/Orrange.png"));
            }
            if(Colour == "LIGHT-GREEN"){
                texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Mob/Slime/Lime-Green.png"));
            }
            if(Colour == "LIGHT-BLUE"){
                texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Mob/Slime/Light-Blue.png"));
            }
            if(Colour == "GREY"){
                texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Mob/Slime/Grey.png"));
            }
            if(Colour == "GREEN"){
                texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Mob/Slime/Green.png"));
            }
            if(Colour == "BLUE"){
                texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/Mob/Slime/Blue.png"));
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public void Render()
    {

        Color.white.bind();
        texture.bind(); // or GL11.glBind(texture.getTextureID());

        GL11.glBegin(GL11.GL_QUADS);
            GL11.glTexCoord2f(0,0);
            GL11.glVertex2f(DX,DY);
            GL11.glTexCoord2f(1,0);
            GL11.glVertex2f(DX+texture.getTextureWidth(),DY);
            GL11.glTexCoord2f(1,1);
            GL11.glVertex2f(DX+texture.getTextureWidth(),DY+texture.getTextureHeight());
            GL11.glTexCoord2f(0,1);
            GL11.glVertex2f(DX,DY+texture.getTextureHeight());
        GL11.glEnd();

    }

    public void Update()
    {
         UP = Keyboard.isKeyDown(Keyboard.KEY_W);
         DOWN = Keyboard.isKeyDown(Keyboard.KEY_S);
         LEFT = Keyboard.isKeyDown(Keyboard.KEY_A);
         RIGHT   = Keyboard.isKeyDown(Keyboard.KEY_D);

         if(UP){Y=Y+SPEED;}
         if(DOWN){Y=Y-SPEED;}
         if(LEFT){X=X+SPEED;}
         if(RIGHT){X=X-SPEED;}
    }

}

I have heard of RECTANGLE collision checking but I have no idea how to implement it into a tile based game.

Thanks, Griff.

share|improve this question
1  
Griff, remember that comment about posting all your code? It's not required, please clean it up. Show us that you at least understand where the issue is and only post the relevant code. – Byte56 Mar 8 '13 at 23:57
up vote 3 down vote accepted

Here's the concept. Lets start with a red square representing your player and a green square representing an obstacle:

enter image description here

Now the player presses right on the keyboard. Here's the part of your code that moves them:

public void Update()
    {
         RIGHT   = Keyboard.isKeyDown(Keyboard.KEY_D);
         if(RIGHT){X=X+SPEED;}
    }

Actually your code says X-SPEED, but that would seem to me to be left, not right, unless your coordinate system is pretty unusual, so I changed it. Anyway, they've pressed right, and lets say SPEED is enough that this creates a collision, so we have this:

enter image description here

But that's what we want to prevent! Don't panic though, it hasn't been displayed to the user yet, so there's still time to fix it. First we need to detect that a collision occurred. We'd do that right after moving the square.

public void Update()
    {
         RIGHT   = Keyboard.isKeyDown(Keyboard.KEY_D);
         if(RIGHT){X=X+SPEED;}
         checkIfCollisionHappened()  //  We're going to check right here.
    }

So what goes on in that checkIfCollisionHappened() method? Well you might take a look at the Rect object. That's a simple way to check. In particular it has a few intersect methods you're going to want to use. I.e., you create a Rect for your player (feed the coordinates in the Rect constructor) and create a Rect for the obstacle and check the intersect method of one against the other.

So lets say it returns true, we have a collision, how do we resolve the collision? The answer is we want to move the player back by the amount that the rectangles intersected, so it arrives here:

enter image description here

Our pseudo-code is going to start looking something like this:

public void Update()
    {
         RIGHT   = Keyboard.isKeyDown(Keyboard.KEY_D);
         if(RIGHT){X=X+SPEED;}
         if (checkIfCollisionHappened())  //  Did we collide?
                adjustForCollision();  // If so move the square back.
    }

So now comes the question, how does the adjustForCollision() method know how much to move the sprite back? Look again at those intersect() methods in Rect and you'll see some of them return a Rect that represents the overlap between the two Rects. So that means we can use that return Rect, take its width, and move the player sprite leftwards accordingly.

The key thing here is that we've moved the sprite back to where it isn't overlapping before any of this got rendered to the player, so the player never knows the red square didn't just "stop" when it hit the green square.

share|improve this answer
    
How can I implement your code? Sorry, I am really bad at things like this. – Griffy Mar 9 '13 at 7:01
    
I gave you a pretty step-by-step walkthrough there, Griffy. What part are you having difficulty with? Do you want to post your attempt at implementing it? – Cameron Fredman Mar 9 '13 at 12:50
    
What goes in the adjustForCollision(); function. I have no idea how to use Rects. – Griffy Mar 10 '13 at 1:27
    
I linked Rect to the javadoc above. Did you make the code for checking for collisions yet (before you adjust for them)? If so please post it and we can discuss further. – Cameron Fredman Mar 10 '13 at 1:33
    
No I have not started writing the collision checking yet. Where is the java doc link? – Griffy Mar 10 '13 at 10:50

I've just figured out a decent system wich works quite perfectly; So, first of in your main update method you manage the inputs like usual:

l = Keyboard.isKeyDown(Input.KEY_A);
r = Keyboard.isKeyDown(Input.KEY_D);
u = Keyboard.isKeyDown(Input.KEY_W);
d = Keyboard.isKeyDown(Input.KEY_S);

if(l){
    setXvel(-speed*delta); 
    //Note: you dont have to use speed*delta if you don't want to or can't
    //But you do need to keep the "-" there
}

if(r){
    setXvel(speed*delta);
}

if(u){
    setYvel(-speed*delta);
}

if(d){
    setYvel(speed*delta);
}

The usuall stuff. Now you need to make a movement method that takes a velocity and move the player by that velocity (just call it move(float xv, float yv) and put x += xv and the same for the y and other collision rectangles and such)

private void move(float xv, float yv){
    x += xv;
    y += yv;

    setBounds(new Rectangle(x,y,16,16));
}

Next you need a set() method wich is almost identical to the move method, just that in set() you'll put x = xv, and again the same for y

private void set(float xv, float yv){
    x = xv;
    y = yv;

    setBounds(new Rectangle(x,y,16,16));
}

Now this is the complicated part; I will put in the code for the colision check method and after that I'll explain it.

private void colc(float xv, float yv){
    Tile in = null;
    for(Tile t : World.tiles){
        if(t.isSolid){
            if(this.getBounds().intersects((t.getBounds()))){
                in = t;
                if(in != null){
                    // first we do horizontal checks;
                    if(xv > 0){
                        // going right
                        if(this.getX() <= in.getBounds().getX()){
                            if(this.getY() + this.getWidth > in.getBounds().getY()){
                                if(this.getY() < in.getBounds().getY() + in.getBounds().getHeight()){
                                    set(in.getBounds().getX() - this.getWidth, this.getY());
                                }
                            }
                        }
                    }
                    if(xv < 0){
                        // going left
                        if(this.getX() + this.getWidth>= in.getBounds().getX() + in.getBounds().getWidth()){
                            if(this.getY() + this.getWidth> in.getBounds().getY()){
                                if(this.getY() < in.getBounds().getY() + in.getBounds().getHeight()){
                                    set(in.getBounds().getX() + in.getBounds().getWidth(), this.getY());
                                }
                            }
                        }
                    }

                    // then we do the vertical checks;
                    if(yv > 0){
                        // going down
                        if(this.getY() <= in.getBounds().getY()){
                            if(this.getX() + this.getHeight> in.getBounds().getX()){
                                if(this.getX() < in.getBounds().getX() + in.getBounds().getWidth()){
                                    set(this.getX(), in.getBounds().getY() - 16);
                                }
                            }
                        }
                    }
                    if(yv < 0){
                        // going up
                        if(this.getY() + this.getHeight>= in.getBounds().getY() + in.getBounds().getHeight()){
                            if(this.getX() + this.getHeight> in.getBounds().getX()){
                                if(this.getX() < in.getBounds().getX() + in.getBounds().getWidth()){
                                    set(this.getX(), in.getBounds().getY() + in.getBounds().getHeight());
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

So, the names for the variables are selfexplainatory, but here's a re-cap:

xv and yv - the velocity and wich the player moves

getBounds - means the rectangle that represents the collision box

this - represents the player itself

Now above you can see that enhanced for loop, that loop goes through an ArrayList of tiles from another class. That's the list of the tiles that you'll be going through, but you'll have to set it up/change it to your needs. Then the finished update method for the player should have this in it;

move(getXvel(), 0);
colc(getXvel(), 0);

move(0, getYvel());
colc(0, getYvel());

After the keyboard input of course

That's that, allthought you'll need to change alot of stuff this is the core code. Your welcome :D

share|improve this answer

Basically you store a 2 dimensional area that is composed of nodes. Each node has the corresponding coordinate of a single tile.

If you have player coordinates that are not discrete (for simplicity: is stored as a float instead of an int) you can check weather or not a position your player wants to stand in by simply rounding the coordinates of the position and getting the corresponding tile from your arrays to check weather or not the entity can stand there "if it would move there".

If you have discrete coordinates for your entities, the problem actually solves itself.

I think the solution you are looking for is to have the logic behind the game and the rendering separate. Have your entity move in coordinates where 1 unit to any direction really means 1 tile in any direction and have it move between these coordinates by giving it a non discrete coordinate description. Standing in position (2.5 , 1.5) means standing "on" Tile (2,1) , collision will be calculated by these coordinates.

Your rendering then iterates across your tiles and draws each with an offset equal to the tile size on screen and it's position (so : tile.getX()*TILE_SIZE ; tile.getY()*TILE_SIZE), with the player coodinates : (int) player.getX()*TILE_SIZE [...]

share|improve this answer
    
Thanks for this but how do I implement it into the game. Sorry if this is a "Write My Code For Me" question but I'm really stuck. :-) – Griffy Mar 8 '13 at 23:13

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.