Search code examples
xna2dmonogame

Scrolling lag during draw


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.


Solution

  • 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.