I have experimented with MonoGame for a bit now and I was wondering if this is possible.
Essentially, I would like to keep my draw call frequency at the display’s refresh rate while updating my game logic at a lower frequency than that. For example, think of a game where I want to update the game logic with only 30 Hz and synchronize the draw calls to my monitor’s refresh rate of, say, 60 Hz with vertical sync. This way, the costly game logic update is only called as often as it needs to be, and I can use the remaining draw calls for interpolation of animations or physics, so that players with monitors that have a higher refresh rate than the game’s update frequency of 30 Hz can enjoy a smoother visual appearance.
It looks like Draw() is only ever called at maximum once for every Update() call. I guess the rationale behind it is that all changes should take place in Update(), so there is no point in drawing a new frame when nothing has changed. As you can see, though, there certainly is a use case for this. Is something like this possible with Monogame without having to rewrite essential parts of the framework’s game loop?
Thanks for the answers, that approach would work, even though it is a rather hacky solution. I have opened an issue on GitHub, maybe something like this can be implemented in the framework some day.
I guess this is not needed as decoupling update from drawing is more about presentation layers and responsibility of the game itself, not framework. I have no problems running my stuff at 15 LPS with as high FPS as I/user wants.
Yes, from a design standpoint, game logic and rendering can be decoupled by the internals of the game. From a technical standpoint, though, it looks like the two can’t be completely decoupled at all unless you are ignoring the names of the two methods and simply use them as you like. There is some connection between the two as in that for every Update() the framwork tries to call one Draw() if possible, but not more. They are not two completely distinctive methods running next to each other (metaphorically, not necessarily in the sense of concurrency). This cannot be altered by the architecture of your game, only circumvented.
The approach that has been mentioned above (that you are maybe using yourself) is far from ideal, either. Imagine I want to account for players that use 144 Hz displays. This way, I have to call Update() 144 times a second in order to make sure that Draw() actually gets called every possible display refresh. If, however, another player only has a 60 Hz monitor, it will still be called that often, unnecessarily doing calculations for frames that cannot be displayed at all. To circumvent this, I would have to incorporate the refresh rate of the player’s display into the approach. As far as I can see, MonoGame does not provide any way to query that (especially in a cross-platform way). Sure, I could just calculate the render frequency myself by looking at how often Draw() is actually called, but this approach fails, if the game doesn’t reach the display’s refresh rate. Even if this would work, why should I have to take some hacky workaround like this if a modern framework could give me the necessary tools to do it properly?
@MontyHimself Real world games don’t get 144 fps, but 60 at most which is already really good. Update() is just a bare bone FrameStart(); and there is no magic to it, no over-engineering. Draw() is there to handle the graphic output, and can get skipped when the frames are getting heavy.
What makes XNA/Monogame great is its simplicity, it’s a minimalistic game framework and this is why I’m using it over unity. You are free to implement anything you want, at the price of some work. There is nothing hacky in @Jjagg 's solution, it’s close to what would implemented if it was out of the box.
Your statement about “real world games” is certainly wrong. You can easily play lesser demanding games with 144 FPS if you have a monitor that supports that refresh rate. You can even play high demanding AAA titles with that framerate if you have strong enough hardware. Not all games are frame-capped per design.
I know that I can modify the frequency with which Update() is called. The code you posted would instruct the framework to try to call Update() 100 times a second, but it would still only call Draw() at maximum 100 times, if possible once per Update(). That is my point.
The second link you posted states that one can use GraphicsDevice.Present() to draw the back buffer onto the screen. I haven’t looked into it as of now, but based on the description, using this means losing vsync which is not acceptable for me.
@Jjagg’s code is simply an implementation of a fixed time step in the Update() method. There is already an option in MonoGame to use a fixed time step and I can set it through a property. There is also an Update() and a Draw() method which, as long as the names haven’t been chosen completely arbitrarily, are for updating the game state and drawing onto the screen respectively. Sure I could ignore that and add my own workaround (which would still not get rid of the disadvantage I pointed out in my previous post), but I don’t think this is the correct solution to this problem.
I also like that MonoGame is a very lightweight and simple framework, but I think it would be better if it would go one step further and let me specify that I want to call Draw() as often as I can. I don’t think that this is too much to ask, especially since adding an option to do this would not affect how things work currently. As I mentioned, there are certainly advantages that this would bring with it.
I don’t consider it hacky. If what MonoGame offers by default is not good enough for you you can always implement your own game loop or look for another framework/engine.
From there you can create a custom implementation, if your changes don’t break anything, and adds more control over the loop, just submit a PR, even if it is not accepted, you’re still sharing it with us
So, just don’t set fixed time step? It will call update and draw as fast as it possibly can. Then you accumulate elapsed time within Update and call your “do expensive game logic update” at whatever interval you decide is appropriate. I don’t see how this is inefficient.
Just because you want update to take up less time so that draws occur more frequently, doesn’t mean you want to entirely skip update - doing so would mean you respond to user input slower and could miss entire button press/release events (as just one example problem).
This is getting more into personal preference, but from my point of view it does not make sense to call Draw more often than Update. I tend to only call actual drawing code from the games Draw function. If I’d have a game that required only animation to be updated every frame and more expensive game logic less frequently (which is rarely the case) I’d just do what I posted previously and run the animation update in Update as well. Though it wouldn’t hurt to support this in MonoGame core I suppose.
If you need to call update faster/as fast than draw, then your update “parameters” should not be relied on the number of times it is called, but relied on time elapsed between 2 calls.