C++/SDL Game Design Guidance

Started by
3 comments, last by firey 10 years, 10 months ago

I have started working on writing a 2D Isometric style game. The idea is to have a persistant world (each tile is unique) and sent from the server where it is then generated (tile object) and then drawn when needed.

The way the game code works right now is that all objects have Draw() and Update() functions that are called every loop by it's parent "Screen" (Static objects, with their own controls/objects/references) Draw() and Update() functions, which in turn are called during the game loop. Basically:


While (!Exited)
ActiveScreen->Update(); -> In turn updating all child objects
ActiveScreen->Draw(); -> In turn drawing all the child objects
While our FPS > Max wait


Inside the Update() functions are things such as positioning, reloading certain objects I free to prevent leaks, checking sdl events (mouse movements, key presses, etc).

Then the Draw() functions determine if that object is visible on the screen, if it is marked visible, if the texture is valid, etc then it runs the SDL Functions to draw it (blit).

The screens Draw() function first clears the screen, runs all the draw functions for the objects that have been programmed then it refreshes the screen so that the changes are visible.

First question, is the above the right concept? I used things I had read/sdl tutorials/way XNA did things to put that bit together. If anyone has a better suggestion I am all ears.

Second question, even though I have the checks to ensure that things show on screen I notice that with a large tile array it slows right down even though it is only displaying a small portion, should I have an array that is filled on the update function that holds all the tile objects instead of looping through all of them at draw time? As each tile is it's own thing and done isometrically I need to redraw each visible tile every time so that they stack properly, so updating only 1 tile at a time isn't doable as it will end up overlapping in the wrong way.

I am sure I will have more questions as I go along. I am pretty experienced with C# and I understand C++ enough to make it work/work right however I am not that well versed in game logic as I do application programming for a living. I want to do this in C++ with SDL instead of using a pre-built solution (XNA,MonoGame,etc) as this will teach me a lot more than using things I am already comfortable with, and a lot of the logic is already handled in.

Sorry if this is in the wrong section, but I didn't feel it belonged in beginners section as I have already got it working, it's just not working as smoothly as I was hoping.

Advertisement

Your problem seems to be that you're doing a brute force method for checking the on-screen visibility for every object in your game. Because of this, your program has to perform calculations for every object during each frame, which is quite costly. You should consider implementing quadtrees or another type of space partitioning structure to reduce the number of calculations needed to check for the visibility of each object.

Yea right now I do something like the following for drawing the tiles for example

In gamescreen.cpp's Draw() function


if (ActiveMap != NULL {
    ActiveMap->Draw();
}
 


Active Map's Draw Function()


for (int i = 0; i < Tiles.size(); i++) {
   if (Tiles->XDrawPos() > -140 && Tiles->XDrawPos() < Client::ActiveWindow->Width &&
      Tiles->YDrawPos() > -140 && Tiles->YDrawPos() < Client::ActiveWindow->Height) {
              Tiles->Draw();
  }
}

 

Tiles Draw() Is basically just an SDL_Blit of the tile rect on the games sdl_surface at the X/Y Draw position.

I'm not entirely sure on how to grid it, unless I do x > X and x < Z = Group 1 and if pos > X and < Z then draw Group 1. Because I would essentially draw 4 grids each time (as you could be halfway between one and another). Doing this would put 90% of the processing as part of the world loading. However I plan on getting tile info from the server which means I will still need to do processing as I load a tile block.

The drawing of the tiles is where I notice the slow down, if I don't have it drawing tiles/large number of tiles it runs fine. Also, with the other visiblity I just use a boolean inside the object ie)


TextboxLogin->Visible = true/false
 


And in the draw I just do an


if (Visible) {
       Draw it;
}
 

Why not use the existing tile system as a grid?
Here is a snippet that maybe useful:


void DrawTilesWithinFrustum(Vec2f worldPos, int windowWidth, int windowHeight)
{
    Vec2f minIndex = worldPos / Vec2f(TILE_SIZE_X, TILE_SIZE_Y);
    Vec2f maxIndex = (worldPos + Vec2f((float)windowWidth, (float)windowHeight)) / Vec2f(TILE_SIZE_X, TILE_SIZE_Y);
    
    // We want to be sure that we draw the tiles that are slightly in the frustum.
    maxIndex.x += 1;
    maxIndex.y += 1;

    int numColumns = (maxIndex.x - minIndex.x) + 1;
    int numRows = (maxIndex.y - minIndex.y) + 1;

    for(int row = 0;row < numRows;++row)
    {
    	for(int column = 0;column < numColumns;++column)
    	{
    		int index = (unsigned int)(minIndex.x + row) + (unsigned int)(minIndex.y + column) * TILE_SIZE_X;
    		Tiles[index]->Draw();
    	}
    }
}

worldPos is the position of the camera. This approach would run a lot faster than bruteforcing each tile. It will only loop through the tiles that you want to draw.

Beware that this code snippet is not tested, so it may include type errors etc.

Good luck.

Why not use the existing tile system as a grid?
Here is a snippet that maybe useful:


void DrawTilesWithinFrustum(Vec2f worldPos, int windowWidth, int windowHeight)
{
    Vec2f minIndex = worldPos / Vec2f(TILE_SIZE_X, TILE_SIZE_Y);
    Vec2f maxIndex = (worldPos + Vec2f((float)windowWidth, (float)windowHeight)) / Vec2f(TILE_SIZE_X, TILE_SIZE_Y);
    
    // We want to be sure that we draw the tiles that are slightly in the frustum.
    maxIndex.x += 1;
    maxIndex.y += 1;

    int numColumns = (maxIndex.x - minIndex.x) + 1;
    int numRows = (maxIndex.y - minIndex.y) + 1;

    for(int row = 0;row < numRows;++row)
    {
    	for(int column = 0;column < numColumns;++column)
    	{
    		int index = (unsigned int)(minIndex.x + row) + (unsigned int)(minIndex.y + column) * TILE_SIZE_X;
    		Tiles[index]->Draw();
    	}
    }
}

worldPos is the position of the camera. This approach would run a lot faster than bruteforcing each tile. It will only loop through the tiles that you want to draw.

Beware that this code snippet is not tested, so it may include type errors etc.

Good luck.



Thanks! The issue that I will face, and I am not sure if it is something your code will be able to account for, is that the tiles are isometric, so everything is angled and the way they are positioned in the array would be like:


9 8 7 6 5 4 3 2 1 0

19 18 17 16 15 14 13 12 11 10
29 28 27 26 25 24 23 22 21 20


However the rows/columns are (right now) 25x25 would it be better for me to do some sort of sectioned vectors? ie vector<Tiles*> TileSect1, 2, 3, 4 (based on how many fit comfortably inside the screen), or I could make them structs and have values such as startX/startY and then the vectors inside. At which point I could just loop through the structs finding startX and startY that is inside my draw area or within 1 width/height out. Then it would be a loop through all the Tiles in the struct drawing each one, as the struct alone would be verified not each tile?

The issue is that even though the tiles are 144x144 they will have some overlap and will not be positioned in a proper Vertical/Horizontal position.

This topic is closed to new replies.

Advertisement