Tiling

6efadd001e53e8bc67eefabc01a4de7e
0
gamer27lv 101 May 07, 2012 at 12:06 c++

Hey guys I am having a really hard time following how to tile out a map, I have found multiple tutorials on the subject but always get tripped up and cannot understand the way any of them explain it. I am beginning to feel pretty stupid.

I thought I would try and start a post and see if I can eventially walk through it and talk it out with you guys.

For now I am just using SDL as my SDK and I have set up a basic project which just creates a white screen. There is an unfinished function with some commented questions concerning the first thing that is confusing to me.

P.S. I drew up the example using an array but I know that is not a good way to represent the map, but.. I don’t know what a good way to represent the map would be. Something is telling me that I should be using vectors, but I can’t find any tutorials tiling with vectors.

I am hoping to clear up that confusion as well and learn the right way. Somebody point me in the right direction please!

#include <iostream>
#include <string>
#include <SDL/SDL.h>

const int SCREEN_W = 800;
const int SCREEN_H = 600;

const int TILE_SIZE  = 64;
const int MAP_W = 3;
const int MAP_H =  3;

int tiles[9] = {0,0,1,
                0,1,0,
                0,0,0 };

SDL_Surface* display = NULL;
SDL_Event event;

struct tileStruct {
int x;
int y;
int type;
};

void SetupTiles(int tiles[]) {
    for(int i = 0; i < MAP_W; i++) {
        for(int j = 0; j < MAP_H; j++) {
                tileStruct tileProperties;
                int z = // ?? how would you calculate the correct array key?
                tileProperties.type = tiles[z];
            //  tileProperties.x = // how is this calculated?
            //  tileProperties.y = // how is this calculated?
        }
    }
}
int main( int argc, char* args[] ) {
    bool done = false;
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1) return 1;
    display = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 32, SDL_SWSURFACE);
    if(!display) return 2;

    while(!done) {
        while(SDL_PollEvent( &event )) {
            if(event.type == SDL_KEYDOWN) {
                switch(event.key.keysym.sym) {
                    case SDLK_ESCAPE:
                        done = true;
                        break;
                    default:break;
                }
            }
        }

        SDL_FillRect( display, &display->clip_rect, SDL_MapRGB(display->format, 0xFF, 0xFF, 0xFF) );
        SDL_Flip( display );
    }
    SDL_FreeSurface( display );
    SDL_Quit();
    return 0;
}

11 Replies

Please log in or register to post a reply.

Fd80f81596aa1cf809ceb1c2077e190b
0
rouncer 104 May 07, 2012 at 13:00

x and y are just what position of the array they are in, if its 3x3 6 would be the first column of the last line, 0 would be the first colomn of the first line.

6efadd001e53e8bc67eefabc01a4de7e
0
gamer27lv 101 May 07, 2012 at 13:27

Hello again, here is my progress so far:

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
using namespace std;
const int SCREEN_W = 800;
const int SCREEN_H = 600;
const int TILE_SIZE  = 64;
const int MAP_W = 3;
const int MAP_H =  3;

SDL_Surface* display = NULL;
SDL_Event event;
vector<int> Map;
void CreateVector() {
    // pretend file load
    int tiles[9] = {0,0,1,
                    0,1,0,
                    0,0,0 };
    for(int i = 0; i < 9; i++ ) {
        Map.push_back(tiles[i]);
    }
}
void DrawTiles() {
    for(int i = 0; i < Map.size(); i++) {
        SDL_Rect offset;
        offset.w = TILE_SIZE;
        offset.h = TILE_SIZE;
        offset.x = (1 + TILE_SIZE) * (i % MAP_W);
        offset.y = (1 + TILE_SIZE) * (i / MAP_H);
        switch(Map.at(i)) {
            case 0:
                SDL_FillRect( display, &offset, SDL_MapRGB( display->format, 0x00, 0x00, 0x00) );
                break;
            case 1:
                SDL_FillRect( display, &offset, SDL_MapRGB( display->format, 0x00, 0x00, 0xFF) );
                break;
            default:break;
        }
    }
}
int main( int argc, char* args[] ) {
    bool done = false;
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1) return 1;
    display = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 32, SDL_SWSURFACE);
    if(!display) return 2;
    CreateVector();
    while(!done) {
        while(SDL_PollEvent( &event )) {
            if(event.type == SDL_KEYDOWN) {
                switch(event.key.keysym.sym) {
                    case SDLK_ESCAPE:
                        done = true;
                        break;
                    default:break;
                }
            }
        }
        SDL_FillRect( display, &display->clip_rect, SDL_MapRGB(display->format, 0xFF, 0xFF, 0xFF) );
        DrawTiles();
        SDL_Flip( display );
    }
    SDL_FreeSurface( display );
    SDL_Quit();
    return 0;
}

I was able to make some sense of doing it this way, what do you all think? is vector the way to go? Does this way make sense to any of you?

I am just drawing filled rectangles, how would some of you alter this to be able to use a multi row sprite sheet of back ground tiles?

6efadd001e53e8bc67eefabc01a4de7e
0
gamer27lv 101 May 07, 2012 at 13:52

But now I am running into problems with the one dimensional vector “array” of tiles…

In my latest progress I added another vector to hold the player position, I initialized the player position vector all to 0’s then changed the very first spot in the vector to 1, so that the first square will be the player position.

It worked great, then I added a Right key press event, to move the player position 1 spot to the right, no problem, it worked fine. Then I added a Left key press, to move the player back to spot one. It works too.

BUT now the problem comes in, I started trying to add the Key Down event and realized I cannot just plus a y by 1.. How would I move down and up through the one dimensional array?

Here is the code so far:

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
using namespace std;
const int SCREEN_W = 800;
const int SCREEN_H = 600;
const int TILE_SIZE  = 64;
const int MAP_W = 3;
const int MAP_H = 3;

SDL_Surface* display = NULL;
SDL_Event event;
vector<int> Map;
vector<int> PlayerPos;

void CreateVector() {
    // fake file load
    int tiles[9] =
{0,0,1,
0,1,0,
0,0,0 };
    for(int i = 0; i < 9; i++ ) {
        Map.push_back(tiles[i]);
    }
    for(int i = 0; i < 9; i++ ) {
        PlayerPos.push_back(0);
    }
    PlayerPos[0] = 1;
}


void DrawTiles() {
    for(int i = 0; i < Map.size(); i++) {
        SDL_Rect offset;
        offset.w = TILE_SIZE;
        offset.h = TILE_SIZE;
        offset.x = (1 + TILE_SIZE) * (i % MAP_W);
        offset.y = (1 + TILE_SIZE) * (i / MAP_H);

        if(PlayerPos[i] == 1) {
            SDL_FillRect( display, &offset, SDL_MapRGB( display->format, 0xFF, 0x00, 0x00) );
        } else {
            switch(Map.at(i)) {
                case 0:
                    SDL_FillRect( display, &offset, SDL_MapRGB( display->format, 0x00, 0x00, 0x00) );
                    break;
                case 1:
                    SDL_FillRect( display, &offset, SDL_MapRGB( display->format, 0x00, 0x00, 0xFF) );
                    break;
                default:break;
            }
        }
    }
}


void MovePlayerRight() {
    for(int i = 0; i < Map.size(); i++) {
        if(PlayerPos.at(i) > 0) {
            if(((i+1)<= Map.size()) && (Map.at(i+1) != 1)) {
                PlayerPos[i] = 0;
                PlayerPos[i+1] = 1;
            }
            break;
        } else continue;
    }
}


void MovePlayerLeft() {
    for(int i = 0; i < Map.size(); i++) {
        if(PlayerPos.at(i) > 0) {
            if(((i-1) > -1) && (Map.at(i-1) != 1)) {
                PlayerPos[i] = 0;
                PlayerPos[i-1] = 1;
            }
            break;
        } else continue;
    }
}


void MovePlayerDown() {

// ???????????????????

}



int main( int argc, char* args[] ) {
    bool done = false;
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1) return 1;
    display = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 32, SDL_SWSURFACE);
    if(!display) return 2;
  

CreateVector();

    while(!done) {
      
       while(SDL_PollEvent( &event )) {
            if(event.type == SDL_KEYDOWN) {
                switch(event.key.keysym.sym) {
                    case SDLK_ESCAPE:
                        done = true;
                        break;
                    case SDLK_RIGHT:
                        MovePlayerRight();
                        break;
                    case SDLK_LEFT:
                        MovePlayerLeft();
                        break;
                    default:break;
                }
            }
        }

        DrawTiles();
        SDL_Flip( display );
        SDL_FillRect( display, &display->clip_rect, SDL_MapRGB(display->format, 0xFF, 0xFF, 0xFF) );
    }

    SDL_FreeSurface( display );
    SDL_Quit();
    return 0;
}
6efadd001e53e8bc67eefabc01a4de7e
0
gamer27lv 101 May 07, 2012 at 14:06

Ok it seems I was able to figure out moving up and down on my tiled map, but is this really a good way of doing it?

void MovePlayerDown() {
    for(int i = 0; i < Map.size(); i++) {
        if(PlayerPos.at(i) > 0) {
            if((i + 3) <= Map.size() && (Map.at(i + MAP_W) != 1)) {
                PlayerPos[i] = 0;
                PlayerPos[i + MAP_W] = 1;
            }
            break;
        } else continue;
    }
}

void MovePlayerUp() {
    for(int i = 0; i < Map.size(); i++) {
        if(PlayerPos.at(i) > 0) {
            if((i - 3) > -1 && (Map.at(i - MAP_W) != 1)) {
                PlayerPos[i] = 0;
                PlayerPos[i - MAP_W] = 1;
            }
            break;
        } else continue;
    }
}
6efadd001e53e8bc67eefabc01a4de7e
0
gamer27lv 101 May 07, 2012 at 14:34

Well I kind of accomplished what I wanted so far, of course it is without graphics, but you can push another tile around like the Sokoban game without any game winning condition…

I am looking for feedback and whether or not I did anything in a bad practice

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
using namespace std;
const int SCREEN_W = 800;
const int SCREEN_H = 600;
const int TILE_SIZE  = 32;
const int MAP_W = 15;
const int MAP_H = 15;
const int TOTAL_TILES = 225;
SDL_Surface* display = NULL;
SDL_Event event;
vector<int> Map;
vector<int> PlayerPos;
void CreateVector() {
    // file load mock
    int tiles[TOTAL_TILES] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                              1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,
                              1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,
                              1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,
                              1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
                              1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,
                              1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,
                              1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,
                              1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,
                              1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,
                              1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,
                              1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,
                              1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,
                              1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,
                              1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  };
    for(int i = 0; i < TOTAL_TILES; i++ ) {
        Map.push_back(tiles[i]);
    }
    for(int i = 0; i < TOTAL_TILES; i++ ) {
        PlayerPos.push_back(0);
    }
    PlayerPos[17] = 1;
    PlayerPos[32] = 2;
}
void DrawTiles() {
    for(int i = 0; i < Map.size(); i++) {
        SDL_Rect offset;
        offset.w = TILE_SIZE;
        offset.h = TILE_SIZE;
        offset.x = (1 + TILE_SIZE) * (i % MAP_W);
        offset.y = (1 + TILE_SIZE) * (i / MAP_H);

        if(PlayerPos[i] == 1) {
            SDL_FillRect( display, &offset, SDL_MapRGB( display->format, 0xFF, 0x00, 0x00) );
        } else if (PlayerPos[i] == 2) {
            SDL_FillRect( display, &offset, SDL_MapRGB( display->format, 0x00, 0xFF, 0x00) );
        } else {
            switch(Map.at(i)) {
                case 0:
                    SDL_FillRect( display, &offset, SDL_MapRGB( display->format, 0x00, 0x00, 0x00) );
                    break;
                case 1:
                    SDL_FillRect( display, &offset, SDL_MapRGB( display->format, 0x00, 0x00, 0xFF) );
                    break;
                default:break;
            }
        }
    }
}
void MovePlayerRight() {
    for(int i = 0; i < Map.size(); i++) {
        if(PlayerPos.at(i) == 1) {
            if(((i+1)<= Map.size()) && (Map.at(i+1) != 1)) {
                if(PlayerPos.at(i+1) == 2) {
                    if(((i+2)<= Map.size()) && (Map.at(i+2) != 1)) {
                    PlayerPos[i+1] = 0;
                    PlayerPos[i+2] = 2;
                    } else break;
                }
                PlayerPos[i] = 0;
                PlayerPos[i+1] = 1;
            }
            break;
        } else continue;
    }
}
void MovePlayerLeft() {
    for(int i = 0; i < Map.size(); i++) {
        if(PlayerPos.at(i) == 1) {
                if(PlayerPos.at(i-1) == 2) {
                    if(((i-2)> -1) && (Map.at(i-2) != 1)) {
                    PlayerPos[i-1] = 0;
                    PlayerPos[i-2] = 2;
                    } else break;
                }
            if(((i-1) > -1) && (Map.at(i-1) != 1)) {
                PlayerPos[i] = 0;
                PlayerPos[i-1] = 1;
            }
            break;
        } else continue;
    }
}
void MovePlayerDown() {
    for(int i = 0; i < Map.size(); i++) {
        if(PlayerPos.at(i) == 1) {
                if(PlayerPos.at(i + MAP_W) == 2) {
                    if((i + (MAP_W * 2) <= Map.size()) && (Map.at(i + (MAP_W * 2)) != 1)) {
                    PlayerPos[i + MAP_W] = 0;
                    PlayerPos[i + (MAP_W * 2)] = 2;
                    } else break;
                }
            if((i + MAP_W) <= Map.size() && (Map.at(i + MAP_W) != 1)) {
                PlayerPos[i] = 0;
                PlayerPos[i + MAP_W] = 1;
            }
            break;
        } else continue;
    }
}

void MovePlayerUp() {
    for(int i = 0; i < Map.size(); i++) {
        if(PlayerPos.at(i) ==  1) {
            if(PlayerPos.at(i - MAP_W) == 2) {
                if((i - (MAP_W * 2) <= Map.size()) && (Map.at(i - (MAP_W * 2)) != 1)) {
                PlayerPos[i - MAP_W] = 0;
                PlayerPos[i - (MAP_W * 2)] = 2;
                } else break;
            }
            if((i - MAP_W) > -1 && (Map.at(i - MAP_W) != 1)) {
                PlayerPos[i] = 0;
                PlayerPos[i - MAP_W] = 1;
            }
            break;
        } else continue;
    }
}

int main( int argc, char* args[] ) {
    bool done = false;
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1) return 1;
    display = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 32, SDL_SWSURFACE);
    if(!display) return 2;
    CreateVector();
    while(!done) {
        while(SDL_PollEvent( &event )) {
            if(event.type == SDL_KEYDOWN) {
                switch(event.key.keysym.sym) {
                    case SDLK_ESCAPE:
                        done = true;
                        break;
                    case SDLK_RIGHT:
                        MovePlayerRight();
                        break;
                    case SDLK_LEFT:
                        MovePlayerLeft();
                        break;
                    case SDLK_DOWN:
                        MovePlayerDown();
                        break;
                    case SDLK_UP:
                        MovePlayerUp();
                        break;
                    default:break;
                }
            }
        }
        DrawTiles();
        SDL_Flip( display );
        SDL_FillRect( display, &display->clip_rect, SDL_MapRGB(display->format, 0xFF, 0xFF, 0xFF) );
    }
    SDL_FreeSurface( display );
    SDL_Quit();
    return 0;
}
6efadd001e53e8bc67eefabc01a4de7e
0
gamer27lv 101 May 07, 2012 at 21:32

I figured I would be pretty much on my own rubber duck debbugging this…

6837d514b487de395be51432d9cdd078
0
TheNut 179 May 07, 2012 at 23:33

Personally I would stick to using a 2D arrays. It’s more intuitive to work with when dealing with grid based problems and you don’t have to deal with offset calculations. I personally have created my own 2D array class, both with and without the observable pattern design. It’s really quite flexible and safe (boundary checks). I’ve used it for path finding, grid based gaming (bejewled type clone), spatial partioning (particularily with heightmaps), large scale image analysis (up to 2TB of image data) and yes, even tile based rendering. I would suggest you write your own 2d array class as well to help you with your game.

Your keyboard inputs should also be more fluid. Right now you move the player only when a key is pressed. What you should have is a state based system. When the “Right” key is pressed, you should set the player’s velocity to (1x, 0y, 0z). In your game loop, you automatically add the player’s velocity to his current position. This way you press the key once and movement continues until the key is released and you reset the player’s velocity back to zero. To get even better results, you should incorporate acceleration.

At some point you may also want to start building objects (classes) for your game. You could stick with C if you like, but IMO I would recommend against it.

6efadd001e53e8bc67eefabc01a4de7e
0
gamer27lv 101 May 08, 2012 at 03:03

Hey TheNut, I really appreciate the feedback. I am a little bit more advanced with programming than this example shows. I do have working experience with classes and have messed with inheritence and polymorphism, however I have trouble picking up new concepts unless I leave out anything unnessesary break it down to only the basics needed to make the functionality work.

I started out with 2 dimensional arrays but now every tutorial and video I find is suggesting to not use 2d arrays for maps and to use 1d array instead saying it is more efficient..

I had a lot of trouble understanding the math making the tiles lay out with the 1 dimensional array, but I am finally wrapping my head around it…

So are you saying now that I should be using a 2d array instead? Will you show me a basic example?

P.S. I do understand creating movement with velocity where you can hold a key down and continuously move, I know that. But for this I was just making it where you basically move through a grid square to square… I was wondering while doing this…How would I use a velocity movement, with a walking animation, yet still make them move in the grid? Am I just confusing myself further?

6837d514b487de395be51432d9cdd078
0
TheNut 179 May 08, 2012 at 10:58

@gamer27lv

I started out with 2 dimensional arrays but now every tutorial and video I find is suggesting to not use 2d arrays for maps and to use 1d array instead saying it is more efficient..

Multidimensional arrays were provided for the very reason to make a developer’s life easier. The compiler will reduce it down to a single dimension and automatically produce the offsets for you. There won’t be any difference in terms of efficiency, other than developer efficiency. Of course, with a high level class to wrap around such logic you wouldn’t need to worry about it.
@gamer27lv

So are you saying now that I should be using a 2d array instead? Will you show me a basic example?

A 2D array class, not necessairly a 2D array. You can still represent the array as 1D internally in the class if you like (using a vector). For example.

// Create a new integer array 100 x 200 units long
Array2D<int> array = new Array2D(100, 200);

// Resize the array. Elements currently in the array will be maintained unless the array is shrinking.
array.Resize(150, 250);

// Get the number of rows in the array (150)
array.GetNumRows();

// Get the number of columns in the array (250)
array.GetNumColumns();

// Set a value in the array. When set, any listeners of the class will be notified of an
// element change for which they can act on such information.
array.Set(row, col, value);

// Get item in the array.
// Optionally could use array[row][column]
int array.Get(row, col);

// Gets the 2D location of a particular element
bool array.IndexOf(element, &row, &col);

// Return a 1D array to either serialize or perform some sort of linear algorithm.
// Useful for doing block transfers (memcpy, memset, fread, etc.)
array.GetBuffer();

To get some ideas of an API, have a look at C#’s Array class on MSDN. I find their APIs feature complete and easy to understand. It serves as a basis for most of the features I’ve implemented. In my class, Array2D inherits from Array and I do the offsets internally. So all of the logic is already done, I’m just effectively writing an adapter. From a development standpoint, working with rows and columns is vastly quicker than picturing in my head where I am and what I’m doing in a 1D array.
@gamer27lv

How would I use a velocity movement, with a walking animation, yet still make them move in the grid?

The walking animation will be in-place, like walking on a treadmill. The legs and arms are moving, but the body is not. In order to translate the model, you must do that in code and that’s where your player’s position, velocity, and acceleration will come into play. The key is finding the right speeds that synchronize with the animation. You will need to experiement a bit to find what looks right.

Fd80f81596aa1cf809ceb1c2077e190b
0
rouncer 104 May 08, 2012 at 12:24

int tile[10000000] thats all you need ;)

6efadd001e53e8bc67eefabc01a4de7e
0
gamer27lv 101 May 10, 2012 at 07:32

thanks again, I am using a 2d vector now..