1

I have started working on a new game using xna/monogame. I have designed a few 2D games before but decided to make this one isometric which is new to me. I have been using reimers isometric tile engine tutorial and most of the drawing code at its simplest level is a copy of this.

When scrolling around the map there is a stuttering on the screen which I can only describe as lag. I have put an FPS counter on and this stays at a solid 60 so I believe this is some kind of graphics problem.

I have tried commenting out sections of the drawing code to narrow down the issue but cant seem to make any progress, however this wasnt happening a couple of weeks ago so I'm confident its something that I have added, perhaps an issue with on the of the tilesets that's causing it?

Any suggestions are appreciated. I haven't put any code here as its turned into quite a large project and I don't know which area to concentrate on but I have uploaded the whole project here if anybody is kind enough to investigate.

Prix
  • 19,417
  • 15
  • 73
  • 132
user3208483
  • 385
  • 5
  • 14
  • Micky, What part would be useful? I tested a bit further last night and it seems to be worse when the game first starts and almost stop after scrolling around for a bit, almost as if its loading the textures the first time you see them and works fine after that point. – user3208483 Mar 09 '16 at 09:07
  • Stuttering in .NET games can be caused by the GC kicking in. Check out the tips by Shawn Hargreaves and Riemer in their respective XNA blogs. As for start-up lag, do you do any form of load-on-demand asset stuff? –  Mar 09 '16 at 09:12
  • Thanks I'll take a look into that. No load-on-demand that I've put in, was wondering if XNA does any itself but I'm fairly sure this isn't the case? – user3208483 Mar 09 '16 at 11:49

1 Answers1

3

Tl;dr You are creating a new Texture2D instance (1x1 pixel) called dummyTexture on each call to the DrawMiniMap method. This is a bad idea, as textures need to be sent to the GPU every time (and then disposed, and collected). Add a field named dummyTexture and fully initialize it inside LoadContent.

But please read the rest of the post to see how to identify these issues.

Your Update method is being called at 60fps, but this doesn't have to be true for the Draw method too. Since Game.IsFixedTimeStep is set to true by default, as soon as Monogame sees that Draw is taking longer than expected to finish, your Draw method will skip frames.

Due to the way your game logic is written right now, this is the only way for Monogame to ensure that all updates are done in fixed time steps.

To get better insight into what's happening, first create an additional set of fps variables for Draw method fps, and render both values on the screen. You will see that Update is drawn at 60 fps, but Draw falls below that.

protected override void Draw(GameTime gameTime)
{
    // similar to what you have inside `Update`
    drawFpsTimer += gameTime.ElapsedGameTime;
    drawFpsCount++;
    if (drawFpsTimer >= TimeSpan.FromSeconds(1))
    { drawFpsTimer = TimeSpan.FromSeconds(0); drawFps = drawFpsCount; drawFpsCount = 0; }

    ...

With this in place it's easy to pinpoint the problematic method inside Draw, and get your fps back to 60.

Also, by setting IsFixedTimeStep to false you can get both Update and Draw to be called in succession, but your Update frame rate will drop too - meaning you should change all your update logic to use gameTime.ElapsedGameTime instead of presuming fixed steps:

protected override void Initialize()
{
    // use variable time step
    this.IsFixedTimeStep = false;
    this.TargetElapsedTime = TimeSpan.FromSeconds(1 / 60.0);
    this.graphics.SynchronizeWithVerticalRetrace = true;

    ...

I prefer variable time step in all my projects, especially when you find out that XNA sometimes takes up 100% CPU time on one core when the default value of Game.IsFixedTimeStep is used, but get ready for some refactoring then.

As for why the frames are being skipped, it seems it's the DrawMiniMap method that's problematic. Commenting this out gives 60 fps update + 60 fps draw rate on my machine.

Upon further inspection, it turns out you are creating a new Texture2D instance (1x1 pixel) called dummyTexture on each call to this method. This is a bad idea. Add a field named dummyTexture and fully initialize it inside LoadContent. You could also slightly speed up this method by making tileColour a member of the Tile class to avoid a bunch of if tests on each iteration, but there are probably many optimizations like this and are perhaps not needed unless you are deploying to mobile devices.

Community
  • 1
  • 1
vgru
  • 49,838
  • 16
  • 120
  • 201
  • Thanks for all the info, very helpful and I will take a look into this. I would note that the minimap code was a new addition and I had the problem before that point. Completely forgot to move the dummy texture though, thanks for the reminder! :p – user3208483 Mar 09 '16 at 11:47
  • 1
    Really nice reading, not everyone would go that far as to download files from websites marked as suspicious and debug/read all but the relevant files to write a detailed answer. – Prix Mar 09 '16 at 11:53
  • @user3218507: well, this is what I see on my machine. The game *is* already strangely cpu heavy for such a seemingly simple game; e.g. if I disable vsync and comment out everything except the `DrawCursor` part (which is where you draw fps), frame rate goes to ~400fps, as expected. But, with and everything being drawn, frame rate is somewhere near 80fps (vsync disabled), which means you don't have much room left. So I would recommend you use a profiler and try to find all other bottlenecks. – vgru Mar 09 '16 at 11:58
  • I have a few ideas what could be causing that. I've been led astray thinking I had a constant 60 fps haha. Thanks for all the help, hopefully I can sort it out now – user3208483 Mar 09 '16 at 12:10
  • @user3218507: if you're using VS2015 community, you have a profiler with it, so use it to avoid optimizing at the wrong places. Also, now that I took a better look at `DrawMiniMap`, it's definitely problematic as it makes a huge number of draws for each pixel on the map. On my machine, it's responsible for pretty much all the CPU spent inside `Draw`. You should rewrite it to draw to an offscreen buffer and update the texture with this data (never create new instances inside `Draw` or `Update`). – vgru Mar 09 '16 at 12:19
  • I have sorted the lag problem out now and made some of the changes you mentioned. You have been a massive help!. One small thing, I used your fps code in the draw method but it still sticks at 60, same as the update fps and never goes any higher.... – user3208483 Mar 09 '16 at 20:18
  • @user3218507: if you've set `this.IsFixedTimeStep` to `false`, then they will both show the same value, even if fps drops below 60. If you left it at default value of `true`, then adding a `Thread.Sleep(5)` inside `Draw` should show the difference (i.e. fps should drop). However, it will never go higher because vsync is on (`SynchronizeWithVerticalRetrace` is `true`), meaning that `Draw` calls are synchronized with your monitor refresh rate (and `TargetElapsedTime` is most likely set to `1 / 60.0`, also). – vgru Mar 10 '16 at 08:40
  • To get the maximum rate, set `SynchronizeWithVerticalRetrace` to `false` (this is what game reviewers do when doing benchmarks, although it will introduce screen tearing at higher-than-vsync framerates). This will give you an idea of how fast these methods can go while testing (I usually have a "maxfps" setting for this purpose). Since you don't seem to have any shaders running, I'd say your game is rather CPU-bound (as expected from a "civilization" game), so (when the time comes and if your fps are inadequate) you'll need to go profile your code to try to reduce loops/allocations and stuff. – vgru Mar 10 '16 at 08:51
  • Ah I follow now, a lot of great advice. Thanks for all the help, really appreciated. – user3208483 Mar 10 '16 at 08:57