Trouble with tiles in SDL

Started by
9 comments, last by Gage64 15 years, 4 months ago
Hey everyone I'm new here. It has been a very long time since I've been into this stuff. I only ever managed one tile engine on the GP32. The problem I'm having is the function DrawTile doesn't seem to be working. I've commented out some code but otherwise I can draw a back ground, and a sprite. Even the tileset. I just can't seem to take tiles from the tile set, use DrawLayer to go through all 10 by 10 (yes very crude right now). Each element it calls DrawTile. Then it's supposed to draw the whole layer to screen. What is wrong guys? #include <stdio.h> #include <stdlib.h> #include <iostream> #include <SDL.h> using namespace std; signed short Tiles1map1MapData[10][10] = {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 2, 3, 3, 4, 0, 0, 0, 1, 5, 3}, { 6, 6, 6, 6, 0, 0, 0, 6, 6, 6}, { 6, 6, 6, 6, 0, 0, 0, 6, 6, 6}, { 6, 6, 6, 6, 4, 0, 0, 6, 6, 6}, { 6, 6, 6, 6, 6, 0, 0, 6, 6, 6}, { 6, 6, 6, 6, 6, 0, 0, 6, 6, 6}}; SDL_Surface * bg; SDL_Surface * t_layer1; SDL_Surface * tiles1; SDL_Surface * sprite; SDL_Surface * screen; int xpos = 0, ypos = 0; int InitImg(); void DrawImg(SDL_Surface * img, int x, int y); void DrawImg(SDL_Surface * img, int x, int y, int w, int h, int x2, int y2); void DrawBG(); void DrawScene(); void DrawLayer(SDL_Surface * layer, SDL_Surface * tileset, signed short MapData[10][10]); void DrawTile(SDL_Surface * layer, SDL_Surface * tileset, int x, int y, int w, int h, int x2); int main(int argc, char *argv[]) { Uint8 * keys; if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0) { cout << "Unable to initialize video. " << SDL_GetError(); exit(1); } atexit(SDL_Quit); screen = SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF); if (screen == NULL) { cout << "Unable to initialize VGA. " << SDL_GetError(); exit(1); } InitImg(); DrawBG(); int done = 0; while(done == 0) { SDL_Event event; while ( SDL_PollEvent(&event) ) { if ( event.type == SDL_QUIT ) { done = 1; } if ( event.type == SDL_KEYDOWN ) { if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; } } } keys = SDL_GetKeyState(NULL); if ( keys[SDLK_UP] ) ypos -= 1; if ( keys[SDLK_DOWN] ) ypos += 1; if ( keys[SDLK_LEFT] ) xpos -= 1; if ( keys[SDLK_RIGHT] ) xpos += 1; if ( keys[SDLK_SPACE] ) { xpos = 0; ypos = 0; DrawBG(); } DrawScene(); } return 0; } int InitImg() { bg = SDL_LoadBMP("bg.bmp"); sprite = SDL_LoadBMP("sprite.bmp"); tiles1 = SDL_LoadBMP("Tiles1.bmp"); //if (bg == NULL) exit(1); //if (sprite == NULL) exit(1); return 0; } void DrawImg(SDL_Surface * img, int x, int y) { SDL_Rect dest; dest.x = x; dest.y = y; SDL_BlitSurface(img, NULL, screen, &dest); } void DrawImg(SDL_Surface * img, int x, int y, int w, int h, int x2, int y2) { SDL_Rect dest; SDL_Rect source; dest.x = x; dest.y = y; source.x = x2; source.y = y2; source.w = w; source.h = h; SDL_BlitSurface(img, &source, screen, &dest); } void DrawBG() { DrawImg(bg,0,0); DrawLayer(t_layer1, tiles1, Tiles1map1MapData); //DrawImg(tiles1,50,50,32,32,64,0); DrawTile(t_layer1, tiles1, 0,0,32,32,0); DrawImg(t_layer1,0,0); //DrawImg(tiles1,0,0); } void DrawScene() { //DrawImg(bg, xpos - 2, ypos - 2, 136,136, xpos - 2, ypos - 2); //DrawImg(t_layer1,0,0); //DrawImg(sprite, xpos, ypos); SDL_Flip(screen); } void DrawLayer(SDL_Surface * layer, SDL_Surface * tileset, signed short MapData[10][10]) { for(int r = 0; r < 10; r++) { for (int c = 0; c < 10; c++) { DrawTile(layer, tileset, c * 32, r * 32, 32, 32, MapData[r][c] * 32); } } } void DrawTile(SDL_Surface * layer, SDL_Surface * tileset, int x, int y, int w, int h, int x2) { SDL_Rect dest; SDL_Rect source; dest.x = x; dest.y = y; source.x = x2; source.y = 0; source.w = w; source.h = h; SDL_BlitSurface(tileset, &source, layer, NULL); }
Advertisement
Quote:
void DrawBG()
{
DrawImg(bg,0,0);
DrawLayer(t_layer1, tiles1, Tiles1map1MapData);
//DrawImg(tiles1,50,50,32,32,64,0);
DrawTile(t_layer1, tiles1, 0,0,32,32,0);
DrawImg(t_layer1,0,0);
//DrawImg(tiles1,0,0);

}

Your first problem is here. You provide a surface for your DrawLayer function to draw on. Unfortunately, you haven't actually created a surface! It's just a NULL pointer and is disregarded immediately. If you want a separate surface to draw the tilemap on, you'll have to create it using SDL_CreateRGBSurface():
SDL_PixelFormat* pf = screen->format;t_layer1 = SDL_CreateRGBSurface(SDL_SWSURFACE, 10 * 32, 10 * 32, pf->BitsPerPixel, pf->Rmask, pf->Gmask, pf->Bmask, pf->Amask);

Remember that this is an intermediate surface though, so you'll have to blit t_layer1 onto the screen at some point before you flip.

Unless you're trying to do something fancy, it will be easier and simpler just to blit onto the buffer itself like this:
DrawLayer(screen, tiles1, Tiles1map1MapData);


The second problem is in the DrawTile function:
SDL_BlitSurface(tileset, &source, layer, NULL);

You didn't provide a destination, so it will be assumed to be 0,0. Every tile will be drawn there. You set the destination rect but didn't use it :)

Replace NULL with &dest in your case.


If I put my 2 cents in and get a penny for my thoughts, where does my other penny go?
After much headache, I realized what you had mentioned. I decided to draw the tiles directly to * screen as a test. I also noticed I made the error that the way I was indexing the tiles was wrong, I just needed a -1 ((MapData[r][c] - 1) * 32).

I only used that NULL instead of *dest in testing to see if I was getting r/c x/y mixed up for some stupid reason but I have already fixed that up too.

I was trying to figure out how to initialize the screen for a while tho. Thanks a lot for that. I was trying SDL_Screen * x = char [32x10x32x10x4].

I noticed where everything was tiled to be index 0 is black (there is no such tile place there. What's a good way around this and how do I use transparency? I've tried adding SDL_SetColorKey(screen, SDL_SRCCOLORKEY, SDL_MapRGB(screen->format, 255, 0, 255));
Quote:Original post by kickstep
how do I use transparency? I've tried adding SDL_SetColorKey(screen, SDL_SRCCOLORKEY, SDL_MapRGB(screen->format, 255, 0, 255));


You should pass the surface that's going to be drawn, not the screen. For example:

SDL_SetColorKey(ship, SDL_SRCCOLORKEY, SDL_MapRGB(ship->format, 255, 0, 255));

Quote:I noticed where everything was tiled to be index 0 is black (there is no such tile place there. What's a good way around this


What do you mean "there is no such tile place there"?
Quote:Original post by kickstep
I was trying to figure out how to initialize the screen for a while tho. Thanks a lot for that. I was trying SDL_Screen * x = char [32x10x32x10x4].

The code I provided doesn't initialize the screen. It creates a new, blank surface in memory with the specified dimensions with the same bit depth as the primary surface.

Quote:
I noticed where everything was tiled to be index 0 is black (there is no such tile place there. What's a good way around this and how do I use transparency? I've tried adding SDL_SetColorKey(screen, SDL_SRCCOLORKEY, SDL_MapRGB(screen->format, 255, 0, 255));

I don't fully understand this part. The very first tile in the tile sheet is black? Or are you seeing the backbuffer coming through? Or is the first tile not being displayed?

As for transparency, your code is applying it to the buffer. You don't want to do that. Instead, set a color key on the surface you'd like to blit. If you have a surface called Foo and the area in Foo you want transparent is magenta, then you could do the following:
SDL_SetColorKey(Foo, SDL_SRCCOLORKEY, SDL_MapRGB(m_primary->format, 255, 0, 255));SDL_Rect myDest; // assume you filled this out as appropriateSDL_BlitSurface(screen, NULL, Foo, &myDest);

And only the non-magenta parts of Foo would be blitted.

If I put my 2 cents in and get a penny for my thoughts, where does my other penny go?
Okay all of my previous problems have been resolved. I have converted the project to use a couple of classes for tilesets and tile layers.

I always had trouble with C++ pointers. I am trying to pass the two dimensional map array to the class so it can render the layer. I do not want to have a static dimension (Map[][32]). How can I just pass the r,c values and then point to them by passing the address to the initial &Map Here is an excerpt of my code of the original map and creating two classes:

signed short MapData[10][10] =
{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 1, 2, 2, 3, 0, 0, 0, 4, 5, 2},
{ 6, 6, 6, 6, 0, 0, 0, 6, 6, 6},
{ 6, 6, 6, 6, 0, 0, 0, 6, 6, 6},
{ 6, 6, 6, 6, 3, 0, 0, 6, 6, 6},
{ 6, 6, 6, 6, 6, 0, 0, 6, 6, 6},
{ 6, 6, 6, 6, 6, 0, 0, 6, 6, 6}};

tileset buster("Tiles1.bmp", 6);
layer layers(buster, &MapData[0][0], 10, 10);


and then from the class:

#include "layer.h"
#include <SDL.h>
#include <iostream>

layer::layer(tileset tiles, signed short * Map, signed short r, signed short c)
{
rows = r;
columns = c;

surface = SDL_CreateRGBSurface(SDL_SWSURFACE, 10 * 32, 10 * 32, 32, 0,0,0,0);
SDL_SetColorKey(surface, SDL_SRCCOLORKEY, SDL_MapRGB(surface->format, 255, 0, 255));

//MapData = new signed short[32*32];
//for (int r = 0; r < 32; r ++)
//{
// for (int c = 0; r < 32; r ++)
// {
// MapData[r*c] = Map[r*c];
// }
//}
MapData = Map;
DrawLayer(surface, tiles, MapData);
}

layer::~layer(void)
{
}


void layer::DrawLayer(SDL_Surface * layer, tileset src, signed short * MapData)
{
for(int r = 0; r < rows; r++)
{
for (int c = 0; c < columns; c++)
{
DrawTile(layer, src, c * 32, r * 32, (*((MapData + r) +c) - 1));
std::cout << (*((MapData + r) + c) -1) << '\t';
}
}

}


Any help is greatly appreciated. Thanks so much so far guys:)
If I understand you correctly, you're asking how you can treat a 1D array as a 2D array. To get the element at row r and column c, you would write:

arr[r * cumCols + c]

Of course, you can also create your own 2D array class that will make this easy.

EDIT: Accidentally wrote numRows instead of numCols...

[Edited by - Gage64 on November 25, 2008 8:58:17 AM]
Gage64 that is essentially it :) Thanks. I think if I can't figure just that out I need to go to bed now and when I wake up spend more time thinking and less time tinkering. Is there a cleaner fix to that? Oh well I suppose after compiler optimizations it's essentially the same deal anyways.
Quote:Original post by kickstep
Is there a cleaner fix to that?


Looks like I misunderstood you after all. A fix to what? What exactly are you trying to do?
No you did not misunderstand me at all :)

I have a 2d array that I'm (struggling) passing to my class.

With your help I simply passed the address (&MapData[0][0]) and then passed r,c and indexed it. All is working well that way.

Sorry when I say cleaner I mean, is it possible to pass it and reference it properly like a 2d array without passing a fixed dimension? I was assuming i could just pass MapData (the address) to signed short ** MapDataX and then reference it by MapDataX[r][c]? I'm getting type mismatches this way. Would it simply be best just to pass it as a 1d array?

Well I have mad a lot of progress on my tile renderer. I owe you a lot of gratitude. One thing that is puzzling my mind and brings me back to when I last made a project of this nature. What is the best way to create a stable speed environment? e.g. Character moves one tile per second (over 60 frames per second ideally). As it stands rendering is completely dependent on processing power and activates when I press a key. I want to be able to have consistent movement speed independent of how fast the computer is, even if FPS dips and I want it rendering without key input. What is a good way to handle this? I have been thinking of creating a separate event handler function and then render function. Any more insight? Thank you.

This topic is closed to new replies.

Advertisement