[.net] [XNA] Performance question (Tile based RPG)

Started by
4 comments, last by Machaira 16 years, 8 months ago
Hello, I'm making a small top down tile-based game with XNA and C#, that I want to be smooth scrolling. The player will always stand in the middle of the map and the map will scroll underneath him, unless he's near the edge of a map, in which case he will walk towards the edge. (If I remember correctly, I think Secret of Mana was similar to this?) I'm using 32x32 tiles, and my whole map is currently 128x128, I have 2 background layers and 2 foreground layers and an attribute layer (for walls/warps/etc). I'm wondering how much of the map I can show at one time and still have it run rather fast (or at least smooth)? I currently am showing a 10x10 section of the map and it seems fine, but i'm wondering how big I could go before the game would start slowing down alot (if it would at all?) my map drawing code is like this

for (int x =0; x<11; x++)
{
   for (int y = 0; y<11; y++)
   {
      DrawTile(currentMap.background1[x,y]);
      DrawTile(currentMap.background2[x,y]);
   }
}
DrawPlayers();
for (int x =0; x<11; x++)
{
   for (int y = 0; y<11; y++)
   {
      DrawTile(currentMap.foreground1[x,y]);
      DrawTile(currentMap.foreground2[x,y]);
   }
}
So this code has to call the 400 DrawTile functions each time the screen is drawn...this seems like alot...but I know computers are rather fast these days.. for example...could I show a 20x20 section and have it scroll with no problem? Also I'm thinking about adding another Background layer, which would add another DrawTile function for each tile... I would try it out...but it's quite a bit of code to change if it wouldn't work properly...Just wondering if this is too much for the computer to handle that quickly. Thanks in Advance, ArchG
Advertisement
As long as you batch the sprite calls, you should be fine. What's inside of the DrawTile method?

One thing to consider doing, is creating one large texture from the smaller ones at startup. This lets you define your maps however you'd like (hard-coded arrays, text files, XML, binary, etc) and then your code will combine all the necessary textures for the ground into one. Then instead of X00 number of draw calls, you have one for the ground. I used this with a Java based tiling system and it worked wonders. Granted XNA is much faster than Java, but it's something to consider.
Quote:I would try it out...but it's quite a bit of code to change if it wouldn't work properly...Just wondering if this is too much for the computer to handle that quickly.

Why not just redraw existing stuff a second time and see if it impedes the speed significantly? For instance, copy the loop after DrawPlayers() or call the whole map drawing routine twice in a row. Sure it is wasteful but it should give you an idea of how far you can take this and it is kind of the purpose of the test anyway.
Evillive2
Generally you do it like this:

SpriteBatch.Begin(...)foreach(column or row)     foreach(row or column)          DrawTile(..., SpriteBatch);foreach(column or row)     foreach(row or column)          DrawEntity(..., SpriteBatch);SpriteBatch.End();


With batching all the draw calls together, you can easily get thousands (10,000+) items being drawn with no framerate drop. You do not want to be doing the following:

foreach(column or row){     foreach(row or column){          SpriteBatch.Begin(...);          DrawTile(...);          SpriteBatch.End();     }}


This is like making one draw with a Vertex Buffer and then resetting the vertices and buffer to a new position and repeating for every tile. Very inefficient way to do it.
Quote:Original post by NickGravelyn
As long as you batch the sprite calls, you should be fine. What's inside of the DrawTile method?

One thing to consider doing, is creating one large texture from the smaller ones at startup. This lets you define your maps however you'd like (hard-coded arrays, text files, XML, binary, etc) and then your code will combine all the necessary textures for the ground into one. Then instead of X00 number of draw calls, you have one for the ground. I used this with a Java based tiling system and it worked wonders. Granted XNA is much faster than Java, but it's something to consider.


I've actually thought about this, but couldn't ever find a way to do it :-). I only really know how to use SpriteBatch, and I don't think that gives me an option of where to draw to.

Quote:Original post by evillive2
Why not just redraw existing stuff a second time and see if it impedes the speed significantly? For instance, copy the loop after DrawPlayers() or call the whole map drawing routine twice in a row. Sure it is wasteful but it should give you an idea of how far you can take this and it is kind of the purpose of the test anyway.


Never even thought of that ;-). Thanks for the tip, that's what I did. I actually just kept doing this until IsGameRunningSlowly turned true. From what I gather that means that the Draw method is not being able to be called 60 times per second, or the target frame rate. (Not sure if that's actually what it means..but I think so, please correct me if i'm wrong)


I knew I should batch my calls together (is what I was doing), just wasn't sure how much it could take, as I tried making my map editor in GDI+...and it could hardly take anything at all...I didn't think the difference would be *this much* different between them. (I do realize GDI+ has lots of nice functionality and works great for regular apps, and wasn't created for games)

Quote:Orignial post by Krisc
With batching all the draw calls together, you can easily get thousands (10,000+) items being drawn with no framerate drop. You do not want to be doing the following:


Thanks for saying this, really re-assuring to know i'll be able to do what I want.
Why do you have a function call inside the loops? And why two sets of loops (unless your map/tile structure requires it)? Why not just have a DrawMap function that draws the whole map? Something like:

void DrawMap(){      spriteBatch.Begin();      for (int x =0; x<11; x++)      {         for (int y = 0; y<11; y++)         {            //this assumes currentMap.background1/2[x,y] is a texture of course            spriteBatch.Draw(currentMap.background1[x,y],...);            spriteBatch.Draw(currentMap.background2[x,y],...);            //if the player is on this tile, draw him            //this assumes currentMap.foreground1/2[x,y] is a texture of course            spriteBatch.Draw(currentMap.foreground1[x,y],...);            spriteBatch.Draw(currentMap.foreground2[x,y],...);         }      }      spriteBatch.End();}


Obviously, without seeing exactly how your map is set up, this might not work, but I believe you can restructure your code a bit.

Former Microsoft XNA and Xbox MVP | Check out my blog for random ramblings on game development

This topic is closed to new replies.

Advertisement