Tilesheets and SDL

Started by
17 comments, last by rip-off 12 years, 10 months ago
My apologies, I wasn't thinking of the sized integer types. Most SDL types are prefixed with SDL_ though (e.g. SDL_Surface).

I see a class declaration for Tile on this Lazy Foo tutorial. I found it with this Google search.
Advertisement
This is the tutorial I am working on, I even linked it in the original post...
I fail to see the problem. Copy the Tile class declaration into a suitable header file. The Tile class is not part of SDL, it is something Lazy Foo provided for the tutorial.
I know, and it is in a separate header file.
I dont know how to explain my problem any further as I already told everything I know.
Can you post your current code, in full, in a separate code box per file (with the file name clearly marked).
Here you go:

game1.cpp
//Headers
#include <string>
#include <sstream>

#include "constants.h"
#include "globals.h"
#include "functions.h"
#include "classes.h"

using namespace std;

int main(int argc, char* args[]) {

//Quit Flag
bool quit = false;

//Initialize
if(init() == false) {
return 1;
}

//Load the files
if(load_files() == false) {
return 1;
}

Tile *tiles[TOTAL_TILES];

//Frame Number Tracker
int frame = 0;

//The FPS Regulator
Timer fps;

//The Frame timer
Timer fps1;

//Timer used to update the caption
Timer update;

//Keeps track of time since last rendering
Timer delta;
delta.start();

//Start the update and frame timer
update.start();
fps1.start();

Man man1;

//Cap FPS : YES / NO
bool cap = true;

clip_tiles();

//Set the tiles
if(set_tiles(tiles) == false) {
return 1;
}

//While the user has not quit
while(quit == false) {
//Start the frame timer
fps.start();

//While there is an event to handle
while(SDL_PollEvent(&event)) {
man1.handle_input();
//If the User Xed the window
if(event.type == SDL_KEYDOWN) {
//Set the proper message surface
switch(event.key.keysym.sym) {
case SDLK_RETURN: cap = (!cap); break;
default: ;
}
} else if(event.type == SDL_QUIT) {
//Quit the program
quit = true;
}
}

//Move the man
man1.move(delta.get_ticks(), tiles);
//Set camera
man1.set_camera();

//Show the tiles
for(int t = 0; t < TOTAL_TILES; t++) {
tiles[t]->show();
}

delta.start();

//fill the screen white
//SDL_FillRect(screen, &screen->clip_rect,SDL_MapRGB(screen->format, 0xFF, 0xFF, 0xFF));

//Show the wall
//SDL_FillRect(screen, &wall, SDL_MapRGB(screen->format, 0x77, 0x77, 0x77));

//Show the man
man1.show();
//Increment the frame counter
frame++;

//If a second has passed since the caption updated
if(update.get_ticks() > 1000) {
//The framerate as a string
stringstream framerate;

//Calculate the fps and create a string
framerate << "FPS: " << frame / (fps1.get_ticks() / 1000.f);

//Create the surface
fpstext = TTF_RenderText_Solid(font, framerate.str().c_str(), textColor);

update.start();
}

//Show fps
apply_surface(530, 5, fpstext, screen, NULL);

//Update the screen
if(SDL_Flip(screen) == -1) {
return 1;
}
//Cap FPS YES/NO?
if((cap == true) && (fps.get_ticks() < 1000 / FRAMES_PER_SECOND)) {
//Sleep remaining frame time
SDL_Delay((1000 / FRAMES_PER_SECOND) - fps.get_ticks());
}
}

//Free the surfaces and quit SDL
clean_up();
return 0;
}


functions.cpp
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
#include <string>
#include "constants.h"
#include "functions.h"
#include "globals.h"

SDL_Surface *load_image(string filename) {
//Temporary storage for the image that's used
SDL_Surface* loadedImage = NULL;
//The optimized image that will be used
SDL_Surface* optimizedImage = NULL;

//Load the image
loadedImage = IMG_Load(filename.c_str());

//If nothing went wrong in loading the image
if(loadedImage != NULL) {
//Create an optimized image
optimizedImage = SDL_DisplayFormat(loadedImage);
//Free the old image
SDL_FreeSurface(loadedImage);
//If the image was optimized fine
if(optimizedImage != NULL) {
//Map the color key
Uint32 colorkey = SDL_MapRGB(optimizedImage->format, 0xFF, 0, 0xFF);
//Set all pink pixels to transparent
SDL_SetColorKey(optimizedImage, SDL_SRCCOLORKEY, colorkey);
}
}
//Return the optimized image
return optimizedImage;
}

bool init() {
//Initialize all SDL Subsystems
if(SDL_Init(SDL_INIT_EVERYTHING) == -1) {
return false;
}

//Set Up the screen
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE);

//If there was an error in setting up the screen
if(screen == NULL) {
return false;
}

//Initialize SDL ttf
if(TTF_Init() == -1) {
return false;
}

//Set the window caption
SDL_WM_SetCaption("GAME 1", NULL);

//If everthing runs fine
return true;
}


bool load_files() {

//Load the images
man = load_image("man.png");

//Open the font
font = TTF_OpenFont("Dina.fon", 10);

//If there was an error loading the man
if(man == NULL) {
return false;
}

//If there was an error loading the font
if(font == NULL) {
return false;
}

//If everything's fine
return true;
}

void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip) {
//Make a temporary rectangle to hold the offsets
SDL_Rect offset;

//Give the offsets to the rectangle
offset.x = x;
offset.y = y;

//Blit the surface
SDL_BlitSurface(source, clip, destination, &offset);
}

void clean_up() {
//Free the surfaces
SDL_FreeSurface(fpstext);
SDL_FreeSurface(man);
SDL_FreeSurface(background);
SDL_FreeSurface(screen);
SDL_FreeSurface(tileSheet);

//Free the tiles
for(int t = 0; t < TOTAL_TILES; t++) {
delete tiles[t];
}

//Close the font
TTF_CloseFont(font);

//Quit SDL_ttf
TTF_Quit();

//Quit SDL
SDL_Quit();
}

bool check_collision(SDL_Rect A, SDL_Rect B) {
//The sides of the rectangles
int leftA, leftB;
int rightA, rightB;
int topA, topB;
int bottomA, bottomB;

//Calculate the sides of Rect A
leftA = A.x;
rightA = A.x + A.w;
topA = A.y;
bottomA = A.y + A.h;

//Calculate the sides of Rect B
leftB = B.x;
rightB = B.x + B.w;
topB = B.y;
bottomB = B.y + B.h;

//If any sides from A are outside of B > !collision
if(bottomA <= topB) {
return false;
}
if(topA >= bottomB) {
return false;
}
if(rightA <= leftB) {
return false;
}
if(leftA >= rightB) {
return false;
}
//If any of the sides of A are inside of B > collision
return true;
}

bool set_tiles(Tile *tiles[]) {
//The tile offsets
int x = 0, y = 0;
//Open the map
ifstream map("lazy.map");
//If loading error
if(map == NULL) {
return false;
}

//Initialize the tiles
for(int t = 0; t < TOTAL_TILES; t++) {
//Determines that kind of tile will be made
int tileType = -1;
//Read tile from map file
map >> tileType;
//If error reading map
if(map.fail() == true) {
//Stop loading map
map.close();
return false;
}
//If the number is a valid tile number
if((tileType >= 0) && (tileType < TILE_SPRITES)) {
tiles[t] = new Tile(x, y, tileType);
}
//if we dont recognize the tile type
else {
//Stop loading map
map.close();
return false;
}
//Move to next tile spot
x += TILE_WIDTH;
//If we have gone too far
if(x >= LEVEL_WIDTH) {
//Move back
x = 0;
//Move to the next row
y += TILE_HEIGHT;
}
}
//close the file
map.close();
//If the map was loaded fine
return true;
}

void clip_tiles()
{
//Clip the sprite sheet
clips[ TILE_RED ].x = 0;
clips[ TILE_RED ].y = 0;
clips[ TILE_RED ].w = TILE_WIDTH;
clips[ TILE_RED ].h = TILE_HEIGHT;

clips[ TILE_GREEN ].x = 0;
clips[ TILE_GREEN ].y = 80;
clips[ TILE_GREEN ].w = TILE_WIDTH;
clips[ TILE_GREEN ].h = TILE_HEIGHT;

clips[ TILE_BLUE ].x = 0;
clips[ TILE_BLUE ].y = 160;
clips[ TILE_BLUE ].w = TILE_WIDTH;
clips[ TILE_BLUE ].h = TILE_HEIGHT;

clips[ TILE_TOPLEFT ].x = 80;
clips[ TILE_TOPLEFT ].y = 0;
clips[ TILE_TOPLEFT ].w = TILE_WIDTH;
clips[ TILE_TOPLEFT ].h = TILE_HEIGHT;

clips[ TILE_LEFT ].x = 80;
clips[ TILE_LEFT ].y = 80;
clips[ TILE_LEFT ].w = TILE_WIDTH;
clips[ TILE_LEFT ].h = TILE_HEIGHT;

clips[ TILE_BOTTOMLEFT ].x = 80;
clips[ TILE_BOTTOMLEFT ].y = 160;
clips[ TILE_BOTTOMLEFT ].w = TILE_WIDTH;
clips[ TILE_BOTTOMLEFT ].h = TILE_HEIGHT;

clips[ TILE_TOP ].x = 160;
clips[ TILE_TOP ].y = 0;
clips[ TILE_TOP ].w = TILE_WIDTH;
clips[ TILE_TOP ].h = TILE_HEIGHT;

clips[ TILE_CENTER ].x = 160;
clips[ TILE_CENTER ].y = 80;
clips[ TILE_CENTER ].w = TILE_WIDTH;
clips[ TILE_CENTER ].h = TILE_HEIGHT;

clips[ TILE_BOTTOM ].x = 160;
clips[ TILE_BOTTOM ].y = 160;
clips[ TILE_BOTTOM ].w = TILE_WIDTH;
clips[ TILE_BOTTOM ].h = TILE_HEIGHT;

clips[ TILE_TOPRIGHT ].x = 240;
clips[ TILE_TOPRIGHT ].y = 0;
clips[ TILE_TOPRIGHT ].w = TILE_WIDTH;
clips[ TILE_TOPRIGHT ].h = TILE_HEIGHT;

clips[ TILE_RIGHT ].x = 240;
clips[ TILE_RIGHT ].y = 80;
clips[ TILE_RIGHT ].w = TILE_WIDTH;
clips[ TILE_RIGHT ].h = TILE_HEIGHT;

clips[ TILE_BOTTOMRIGHT ].x = 240;
clips[ TILE_BOTTOMRIGHT ].y = 160;
clips[ TILE_BOTTOMRIGHT ].w = TILE_WIDTH;
clips[ TILE_BOTTOMRIGHT ].h = TILE_HEIGHT;
}

bool touches_wall(SDL_Rect box, Tile *tiles[]) {
//Go through the tiles
for(int t = 0; t < TOTAL_TILES; t++) {
//If the tile is a wall type tile
if((tiles[t]->get_type() >= TILE_CENTER) && (tiles[t]->get_type() <= TILE_TOPLEFT)) {
//If the collision box touches the wall tile
if(check_collision(box, tiles[t]->get_box() == true)) {
return true;
}
}
}
//If no wall tiles were touched
return false;
}


man.cpp
//The headers
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include <string>
#include "constants.h"
#include "classes.h"
#include "functions.h"
#include "globals.h"

Man::Man() {
//Initialize the offsets
box.x = 0;
box.y = 0;

//Man's dimensions
box.w = MAN_WIDTH;
box.h = MAN_HEIGHT;

//Initialize velocity
xVel = 0;
yVel = 0;
}

void Man::handle_input() {
//if a key was pressed
if(event.type == SDL_KEYDOWN) {
//Adjust velocity
switch(event.key.keysym.sym) {
case SDLK_UP: yVel -= MAN_VEL; break;
case SDLK_DOWN: yVel += MAN_VEL; break;
case SDLK_LEFT: xVel -= MAN_VEL; break;
case SDLK_RIGHT: xVel += MAN_VEL; break;
default: ;
}
}
//If a key was pressed
if(event.type == SDL_KEYUP) {
//Adjust velocity
switch(event.key.keysym.sym) {
case SDLK_UP: yVel += MAN_VEL; break;
case SDLK_DOWN: yVel -= MAN_VEL; break;
case SDLK_LEFT: xVel += MAN_VEL; break;
case SDLK_RIGHT: xVel -= MAN_VEL; break;
default: ;
}
}
}

void Man::move(Uint32 deltaTicks, Tile *tiles[]) {
//Move the dot horizontally
box.x += xVel * (deltaTicks / 1000.f);
//If the dot went too far > move back
if(box.x < 0) {
box.x = 0;
} else if(box.x + MAN_WIDTH > LEVEL_WIDTH) {
box.x = LEVEL_WIDTH - MAN_WIDTH;
}

//Horizontal Collision detection
if(touches_wall(box, tiles)) {
//Man coming from left
if(((box.x + MAN_WIDTH) >= tiles.x) && (box.x <= tiles.x) && ((box.y >= tiles.y) || (box.y + MAN_HEIGHT) <= (tiles.y + tiles.h))) {
box.x = tiles.x - MAN_WIDTH;
}
//Man coming from right
if((box.x <= (tiles.x + tiles.w)) && (box.x >= tiles.x) && ((box.y >= tiles.y) || (box.y + MAN_HEIGHT) <= (tiles.y + tiles.h))) {
box.x = tiles.x + tiles.w;
}
}

//Move the dot vertically
box.y += yVel * (deltaTicks / 1000.f);
//If the dot went too far > move back
if(box.y < 0) {
box.y = 0;
} else if(box.y + MAN_HEIGHT > LEVEL_HEIGHT) {
box.y = LEVEL_HEIGHT - MAN_HEIGHT;
}

//Vertical collision
if(touches_wall(box, tiles)) {
//Man coming from the top
if((box.y + MAN_HEIGHT >= tiles.y) && (box.y <= tiles.y) && ((box.x >= tiles.x) || (box.x + MAN_WIDTH) <= (tiles.x + tiles.w))) {
box.y = tiles.y - MAN_HEIGHT;
}
//Man coming from the bottom
if((box.y <= (tiles.y + tiles.h)) && (box.y >= tiles.y) && ((box.x >= tiles.x) || (box.x + MAN_WIDTH) <= (tiles.x + tiles.w))) {
box.y = tiles.y + tiles.h;
}
}
}

void Man::set_camera() {
//Center the camera over the man
camera.x = (box.x + MAN_WIDTH / 2) - SCREEN_WIDTH / 2;
camera.y = (box.y + MAN_HEIGHT / 2) - SCREEN_HEIGHT / 2;

//Keep the camera in bounds
if(camera.x < 0) {
camera.x = 0;
}
if(camera.y < 0) {
camera.y = 0;
}
if(camera.x > LEVEL_WIDTH - camera.w) {
camera.x = LEVEL_WIDTH - camera.w;
}
if(camera.y > LEVEL_HEIGHT - camera.h) {
camera.y = LEVEL_HEIGHT - camera.h;
}
}

void Man::show() {
apply_surface((int)box.x - camera.x, (int)box.y - camera.y, man, screen, NULL);
}


tiles.cpp


Tile::Tile(int x, int y, int tileType) {
//Get the offsets
box.x = x;
box.y = y;
//Set the collision box
box.w = TILE_WIDTH;
box.h = TILE_HEIGHT;
//Get the tile type
type = tileType;
}

void Tile::show() {
//If the tile is on screen
if(check_collision(camera, box) == true) {
//Show the tile
apply_surface(box.x - camera.x, box.y - camera.y, tileSheet, screen, &clips[type]);
}
}

int Tile::get_type() {
return type;
}

SDL_Rect Tile::get_box() {
return box;
}


global.cpp
//Headers
#include "SDL/SDL.h"
#include "globals.h"
#include "SDL/SDL_ttf.h"
#include "constants.h"

//The surfaces
SDL_Surface *fpstext = NULL;
SDL_Surface *man = NULL;
SDL_Surface *background = NULL;
SDL_Surface *screen = NULL;

//Event structures
SDL_Event event;

//The font that's going to be used
TTF_Font *font = NULL;

//The Color of the text
SDL_Color textColor = {128, 128, 128};

//The wall
SDL_Rect wall;

SDL_Rect camera = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};



timer.cpp
#include "SDL/SDL.h"
#include "classes.h"

Timer::Timer() {
//Initialize the variables
startTicks = 0;
pausedTicks = 0;
paused = false;
started = false;
}

void Timer::start() {
//start the timer
started = true;

//Unpause the timer
paused = false;

//Get the current clock time
startTicks = SDL_GetTicks();
}

void Timer::stop() {
//Stop the timer
started = false;

//Unpause the timer
paused = false;
}

int Timer::get_ticks() {
//If the timer is running
if(started == true) {
//if the timer is paused
if(paused == true) {
//Return the number of ticks when the timer was paused
return pausedTicks;
} else {
//Return the current time minus the start time
return SDL_GetTicks() - startTicks;
}
}
//If the timer is not running
return 0;
}

void Timer::pause() {
//If the timer is running and is not already paused
if((started == true) && (paused == false)) {
//Pause the timer
paused = true;

//Calculate the paused ticks
pausedTicks = SDL_GetTicks() - startTicks;
}
}

void Timer::unpause() {
//If the timer is paused
if(paused == true) {
//Unpause the timer
paused = false;

//Reset the starting ticks
startTicks = SDL_GetTicks() - pausedTicks;

//Reset the paused ticks
pausedTicks = 0;
}
}

bool Timer::is_started() {
return started;
}

bool Timer::is_paused() {
return paused;
}


classes.h
#ifndef CLASSES_H
#define CLASSES_H

#include "SDL/SDL.h"

//The man that will move around on the screen
class Man {
private:
//The collision box
SDL_Rect box;
//The velocity
float xVel, yVel;

public:
//Initialize all variables
Man();
//Takes key presses and adjusts the man's velocity
void handle_input();
//Move the man
void move(Uint32 deltaTicks, Tile *tiles[]);
//Show on screen
void show();
//Sets the camera over the man
void set_camera();
};

class Tile {
private:
//The attributes of the tile
SDL_Rect box;
//The tile type
int type;

public:
//Initializes the variables
Tile(int x, int y, int tileType);
//Shows the tile
void show();
//Get the tile type
int get_type();
//Get the collision box
SDL_Rect get_box();
};

class Timer {
private:
//The clock time when the timer started
int startTicks;

//The ticks stored when the timer was paused
int pausedTicks;

//The timer status
bool paused;
bool started;

public:
//Initializes variables
Timer();

//The various clock actions
void start();
void stop();
void pause();
void unpause();

//Gets the timer's time
int get_ticks();

//Checks the status of the timer
bool is_started();
bool is_paused();
};

#endif


constants.h
#ifndef CONSTANTS_H
#define CONSTANTS_H

//Screen constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;

//FPS
const int FRAMES_PER_SECOND = 30;

//Dimensions of the level
const int LEVEL_WIDTH = 1280;
const int LEVEL_HEIGHT = 960;

//Dimensions of the man
const int MAN_WIDTH = 16;
const int MAN_HEIGHT = 16;
const int MAN_VEL = 200;

//Tile constants
const int TILE_WIDTH = 80;
const int TILE_HEIGHT = 80;
const int TOTAL_TILES = 192;
const int TILE_SPRITES = 12;

//The different tile sprites
const int TILE_RED = 0;
const int TILE_GREEN = 1;
const int TILE_BLUE = 2;
const int TILE_CENTER = 3;
const int TILE_TOP = 4;
const int TILE_TOPRIGHT = 5;
const int TILE_RIGHT = 6;
const int TILE_BOTTOMRIGHT = 7;
const int TILE_BOTTOM = 8;
const int TILE_BOTTOMLEFT = 9;
const int TILE_LEFT = 10;
const int TILE_TOPLEFT = 11;

#endif


functions.h
#ifndef FUNCTIONS_H
#define FUNCTIONS_H

//Headers
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include <string>

using namespace std;

//File Loader
SDL_Surface *load_image(string filename);

//Surface Blitter
void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip);

//Initialization
bool init();

//File loading
bool load_files();

//Clean up
void clean_up();

//Check collision
bool check_collision(SDL_Rect A, SDL_Rect B);

//Clip the tilesheet
void clip_tiles();

//Set the tiles
bool set_tiles(Tile *tiles[]);

bool touches_wall(SDL_Rect box, Tile *tiles[]);


#endif


globals.h
#ifndef GLOBALS_H
#define GLOBALS_H

//Headers
#include "SDL/SDL.h"
#include "SDL/SDL_ttf.h"
#include "SDL/SDL_image.h"

//The surfaces
extern SDL_Surface *fpstext;
extern SDL_Surface *man;
extern SDL_Surface *background;
extern SDL_Surface *screen;
extern SDL_Surface *tileSheet;

//Event structures
extern SDL_Event event;

//The font that's going to be used
extern TTF_Font *font;

//The Color of the text
extern SDL_Color textColor;

//The wall
extern SDL_Rect wall;

//The camera
extern SDL_Rect camera;

#endif
In classes.h move the "class Tile {}" above "class Man {}"

You are using the Tile class in the Man class, but Man doesn't know it exists yet because it doesn't get defined until afterward.


[EDIT]
Like this:


#ifndef CLASSES_H
#define CLASSES_H

#include "SDL/SDL.h"


class Tile {
private:
//The attributes of the tile
SDL_Rect box;
//The tile type
int type;

public:
//Initializes the variables
Tile(int x, int y, int tileType);
//Shows the tile
void show();
//Get the tile type
int get_type();
//Get the collision box
SDL_Rect get_box();
};

//The man that will move around on the screen
class Man {
private:
//The collision box
SDL_Rect box;
//The velocity
float xVel, yVel;

public:
//Initialize all variables
Man();
//Takes key presses and adjusts the man's velocity
void handle_input();
//Move the man
void move(Uint32 deltaTicks, Tile *tiles[]);
//Show on screen
void show();
//Sets the camera over the man
void set_camera();
};


class Timer {
private:
//The clock time when the timer started
int startTicks;

//The ticks stored when the timer was paused
int pausedTicks;

//The timer status
bool paused;
bool started;

public:
//Initializes variables
Timer();

//The various clock actions
void start();
void stop();
void pause();
void unpause();

//Gets the timer's time
int get_ticks();

//Checks the status of the timer
bool is_started();
bool is_paused();
};

#endif
Thanks! That helped, and I saw that I forgot the headers in tiles.cpp.
I have to redo the collision checking, as this code was from an earlier tutorial, that I came up with in that special case.
Anyways, I got another error, which I couldnt fix now:

C:\game1\tiles.cpp: In member function 'void Tile::show()':
C:\game1\tiles.cpp:25: error: 'clips' was not declared in this scope

clips as it is, are a bunch of declarations inside void clip_tiles() in function.cpp

How would I do this?
I don't even see where clips is actually defined anywhere?

Try this:
Define clips in functions.h (SDL_Rect clips[12] or whatever)
Include functions.h (and Tiles.h :)) in Tiles.cpp

That will define your clips and give your tiles class access to it.
If you read and understood the article I linked in my first reply, the solution should have been apparent. Also my suggestion in this reply could have solved your problem. Later you said the class was in a separate header file, it wasn't: it was in the same one has class Man.

All these things were obstacles to finding your solution. In future, please be clearer and more upfront about what you did and whether you understand the responses your getting.

This topic is closed to new replies.

Advertisement