Draw() not updating (or there's a lot of computation without drawing)

My game is what I call a ‘phased’ game. That is to say, I move, the AI moves and then everything is resolved. The problem that I’m having is that during the ‘while everything is resolved’ phase, nothing is getting updated to the screen.

For example. I’ve given a bunch of orders and now I click on End Turn:

And after about 15 seconds:

As you can see, the units have moved, the ‘Fog of War’ has been updated, lots of stuff behind the scenes have happened BUT what didn’t happen is a series of text draws to the top of the screen telling the user what’s going on (it’s fine for the game to go away for 20 seconds IF it’s telling the user what’s going on and has a thermometer; which I’ll add eventually).
Here’s a code snippet from Draw():

 if (EndTurn)
                {
                    Display.DisplayBanner(spriteBatch, TopBannerSmythe, "Calculating Movement...");

                    ProcessTurn.ProcessEndTurn();

                    // Probably a ton of stuff that needs to be reset.
                    CurrentGameTime += TimeSpan.FromMinutes(MinutesPerTurn);
                    CurrentGameTurn++;

                    Display.DisplayBanner(spriteBatch, TopBannerSmythe, "Calculating Strategic AI...");

                    RedSAIDS = Loading.LoadRedSAIDS();
                    BlueSAIDS = Loading.LoadBlueSAIDS();

                    Display.DisplayBanner(spriteBatch, TopBannerSmythe, "Calculating Fog of War...");

                    if (ActiveArmy)
                    {
                        TacticalAILib.FogOfWar.CalculateFoW(true, BlueArmy, RedArmy, FogOfWar, VisibilityMeters);
                    }
                    else
                    {
                        TacticalAILib.FogOfWar.CalculateFoW(false, BlueArmy, RedArmy, FogOfWar, VisibilityMeters);
                    }

                    HideInvisibleUnits(BlueArmy, RedArmy);
                    Display.DisplayBanner(spriteBatch, TopBannerSmythe, "Calculating Range of Influence...");

                    ROI.CalculateROI(BlueArmy, Map.MetersPerPixel);
                    ROI.CalculateROI(RedArmy, Map.MetersPerPixel);


                    if (IsSimulation)
                    {
                        Display.DisplayBanner(spriteBatch, TopBannerSmythe, "Calculating Courier Paths...");
                        CalculateCourierPaths();
                    }
                    
                    SetupCorpsLists();

                    DisplayFoW();
                    DrawUnits();
                    
                    Mouse.SetCursor(MouseCursor.Arrow);

                    EndTurn = false;
                } /// Here endeth End Turn

And here is a snippet from Update()

 if (lastMouseState.LeftButton == ButtonState.Released
                    && currentMouseState.LeftButton == ButtonState.Pressed
                    && TabIndex == (int)TabNames.Checklist // Checklist
                    && ms.X < LeftMapOffset
                    && ms.Y > 800)
                {
                    // clicked on End Turn
                    EndTurn = true;
                    GivingOrders = false;

                    TabIndex = (int)TabNames.Scenario;

                    Mouse.SetCursor(MouseCursor.Wait);

                }// clicked on End Turn

And that assignment should change the tab to the Scenario Tab (2nd image, above) but it doesn’t. So, maybe that’s a clue, too.

Do you see all those Display.DisplayBanner() calls? That should be putting all these text messages up on the top of the screen where “Note: that all streams on this map are marked as mud terrain,” is. That’s how it got up there (by using DisplayBanner().

So, clearly, Draw() is getting bogged down or something. Any suggestions?
Again, thanks for all the help. This forum - and MonoGame - rocks.

Without knowing much about your code and what it does, I guess my question is… what’s happening while the AI is processing? Like, is it just churning away and those 15 seconds are actual computation, or is the work to play out the AI turn relatively quick and it’s just a 15 second timer that counts down (that you deliberately set) until it’s the player’s turn again?

One way to find out would be in your main update loop, throw a timer around it and measure how much time each update is taking. If, on the AI turn, your update goes from what will normally be a very small number to a very large number, that’s probably a pretty good indicator that a single update is stuck churning through the AI turn (and therefore blocking your draw call from occurring).

If this is happening, you’ll need to find a way to break up your AI turn work. Either utilizing multithreaded techniques, or some sort of time slicing technique (ie, dedicating a maximum amount of time, per frame, to AI until all AI work is complete).

Like I said, I’m not really sure what’s happening, these are just my guesses. Hopefully that helps though :slight_smile:

I guess what my question is:

Is it possible for all these AI methods to execute without any of the intervening draw() methods being called?

So, it’s definitely calculating ‘Fog of War’ because I can see the gray areas update on the battlemap. But, the spritebatch.drawstring calls in between never happen. Even though, when I set a breakpoint, it triggers the break. It’s like the system is saying, “uh, uh not going to do it.”

I think the solution is to move all the methods to Update() and set a boolean so that the DrawBanner calls will happen in Draw(). But, I’m too tired to implement that tonight. Will try and update tomorrow.

Essentially, that’s my question for you. Is your AI update logic so expensive you’re blocking your game for the duration? If you’re seeing animation happen on your screen, the answer is likely no.

Is your condition in the above code, EndTurn, isn’t getting set to true?

I’m not sure what you mean here. I would expect all update logic (including state changes) to occur in Update and all drawing code (ie, the strings) to happen in Draw.

Sleep sounds like a good idea :wink:

Here’s the plan I tried without any success: I moved all the computation to Update() and it’s setting boolean flags in Draw() but I’m never getting the type drawn on the screen.

Here’s the revamped Update code:

  if (lastMouseState.LeftButton == ButtonState.Released
                    && currentMouseState.LeftButton == ButtonState.Pressed
                    && TabIndex == (int)TabNames.Checklist // Checklist
                    && ms.X < LeftMapOffset
                    && ms.Y > 800)
                {
                    // clicked on End Turn
                    EndTurn = true;
                    GivingOrders = false;

                    TabIndex = (int)TabNames.Scenario;

                    Mouse.SetCursor(MouseCursor.Wait);

                    DisplayCalculatingMovement = true;
                            
                    ProcessTurn.ProcessEndTurn();

                    CurrentGameTime += TimeSpan.FromMinutes(MinutesPerTurn);
                    CurrentGameTurn++;

                    DisplayCalculatingFoW = true;
                   
                    if (ActiveArmy)
                    {
                        TacticalAILib.FogOfWar.CalculateFoW(true, BlueArmy, RedArmy, FogOfWar, VisibilityMeters);
                    }
                    else
                    {
                        TacticalAILib.FogOfWar.CalculateFoW(false, BlueArmy, RedArmy, FogOfWar, VisibilityMeters);
                    }

                    HideInvisibleUnits(BlueArmy, RedArmy);

                    DisplayCalculatingROI = true;

                    ROI.CalculateROI(BlueArmy, Map.MetersPerPixel);
                    ROI.CalculateROI(RedArmy, Map.MetersPerPixel);


                    if (IsSimulation)
                    {
                        DisplayCalculatingPaths = true;
                        CalculateCourierPaths();
                    }

                    SetupCorpsLists();

                    EndTurn = false;
                    Mouse.SetCursor(MouseCursor.Arrow);


                }// clicked on End Turn

And here’s the updated Draw code that never fires:

  if (EndTurn)
                {
                    if(DisplayCalculatingMovement)
                        Display.DisplayBanner(spriteBatch, TopBannerSmythe, "Calculating Movement...");

                    if(DisplayCalculatingAI)
                        Display.DisplayBanner(spriteBatch, TopBannerSmythe, "Calculating Strategic AI...");

                    if (DisplayCalculatingFoW)
                        Display.DisplayBanner(spriteBatch, TopBannerSmythe, "Calculating Fog of War...");

                    if(DisplayCalculatingROI)
                        Display.DisplayBanner(spriteBatch, TopBannerSmythe, "Calculating Range of Influence...");

                    
                    if (IsSimulation)
                    {
                        if (DisplayCalculatingPaths)
                            Display.DisplayBanner(spriteBatch, TopBannerSmythe, "Calculating Courier Paths...");

                    }
                    DisplayFoW();
                    DrawUnits();

                } /// Here endeth End Turn

I set breakpoints and I can see it hitting these draw calls but nothing draws.

What is the correct method to get stuff on the screen (just type) while a bunch of number crunching is going on in the background?

Update & Draw are just called one after each other (with the possibility that Update is called multiple times depending on FixedTimeStep.

So if your Update Method takes 2 seconds to finish, the screen will be frozen for 2 seconds - the same is true for draw - if it blocks for 2 seconds, the screen blocks for 2 seconds.

To circument that, you either have to implement stepped logic or do the heavy stuff asyncronously, syncronize data and do handle the drawing upon the current state.

But I am just guessing here, that you basically block the render loop for too long

Yes, I think that’s what’s going on. I really didn’t have a grasp of the relationship between Update() and Draw(). I think I need to break up the chain of method calls in Update and keep sending it back to Draw() to update the display and then come back to Update and hit the next method… Repeat until done.

It may help if you imagine the AI being a real player - how would a real player react. So if it’s his turn you basically just lock the UI, so the real player cannot interact while the AI has its turn.

Now the AI will think about possible turns (async if that takes longer than the 16ms you have per frame). Now all the moves the AI will do are collected in some sort of list. Once you are ready to “play out” all the collected moves, your update function will just iterate tru them, move them per frame or whatever until every move has finished. then you give control back to the player.

The generell idea is, that the renderloop will never be blocked, it just runs and renders the current state, whatever that is. If a unit moves from A to B over 2 seconds you still render 60 frames per seconds, but the unit is on a different spot for every frame. So either interpolate in draw or move it a fraction in every Update

Okay, that’s not exactly the problem. Actually, no AI is being called, yet. The problem is that I want to display messages on the screen saying what’s happening off screen (Calculating paths…, Calculating Fog of War… etc.).

BUT, here’s the deal, each of these methods are definitely taking longer than 16ms. No doubt about that. So, what do I do? What is the async process?

Thanks for the help!

If you wanna move a unit along a path or display a text - it’s the same. You just display a distinct state at the time of the frame.

I could be wrong, but Monogame does not provide any async helpers, so it’s your job as a coder to care for that. Either spawn your own thread or work your way with the C# async keyword - there is multiple ways to go.

The basic idea is just to have the heavy stuff in a thread or async and just let the Draw call render whatever state you wanna display. you may want to have some sort of message queue/list for the strings you wanna display which is populated by the thread and just displayed/animated in draw while the AI is acting. There is no single way to do it.

Yes, I’m seeing how this works. The game animation is all just fine. The only problem I’m having is throwing up a series of messages telling the user what’s going on and, eventually, a thermometer, too. I’ve been a game designer for 35 years and I know that a user will wait 20-30 seconds between turns if they know what’s going on.

Anyway, the problem is that I have methods that go away for about 4-5 seconds and the graphics are not updating (displaying the messages). I haven’t been able to find much about async and Monogame or examples of somebody using it.

I think this is the type of thing you’re looking for:

bool startTheLongProcess = false;
bool doingTheLongProcess = false;


void Update()
{
	if (startTheLongProcess)
	{
		startTheLongProcess = false;
		doingTheLongProcess = true;

		Task.Run(() =>
		{
			// this code will run in a separate thread, and so your Update()/Draw() cycles will continue while this runs separately

			// do your long process stuff here
			// WARNING!! Anything that happens in here must be thread-safe! thread-unsafe code is very difficult to debug (i.e. when this thread and the main thread both try to write to the same var or something - won't always happen, but when it does your game will just crash or malfunction and you won't have any idea why)

			doingTheLongProcess = false;
		});
	}

	if (doingTheLongProcess)
	{
		// run updates that should happen during the running of the long process, like updating animations/messaging to the user about the long process
	}
	else
	{
		// normal update stuff here

		// within this normal updates, you hit the condition that needs your long process

		// trigger your condition for the long process
		startTheLongProcess = true; // the reason you'd do this instead of just starting the long process here is you don't want to risk making thread-unsafe code by starting a process here and potentially having something else within these normal update scripts that could be writing to the same vars and whatever else. Using this method, if you ensure that you only ever have changes to common vars within this else and within the long process's separate task, you won't need to worry about using locks and what not since they could never be running at the same time.
	}
}


void Draw()
{
	if (doingTheLongProcess)
	{
		// display whatever message/animation/etc to the user about the long process running
	}
}
1 Like

First, WOW! This is what I need. However, after adding:

using System.Threading;
using System.Threading.Tasks;

it’s throwing this error:

Error	CS1955	Non-invocable member 'Task' cannot be used like a method.	

I’ve already implemented a series of ‘boolean toggles’ to display the various messages I need displayed. This is the current snippet from Update():

if (EndTurn)
            {
                if (DisplayCalculatingMovement)
                {                   
                    // This needs to be spun off to its own thread
                    ProcessTurn.ProcessEndTurn();

                    CurrentGameTime += TimeSpan.FromMinutes(MinutesPerTurn);
                    CurrentGameTurn++;
                }

                else if (!DisplayCalculatingMovement && DisplayCalculatingFoW)
                {
                    if (ActiveArmy)
                    {
                        // This needs to be spun off to its own thread
                        TacticalAILib.FogOfWar.CalculateFoW(true, BlueArmy, RedArmy, FogOfWar, VisibilityMeters);
                    }
                    else
                    {
                        // This needs to be spun off to its own thread
                        TacticalAILib.FogOfWar.CalculateFoW(false, BlueArmy, RedArmy, FogOfWar, VisibilityMeters);
                    }

                    HideInvisibleUnits(BlueArmy, RedArmy);

                    DisplayCalculatingFoW = false;
                    DisplayCalculatingROI = true;
                }
                else if (!DisplayCalculatingFoW && DisplayCalculatingROI)
                {
                    // This needs to be spun off to its own thread
                    ROI.CalculateROI(BlueArmy, Map.MetersPerPixel);
                    ROI.CalculateROI(RedArmy, Map.MetersPerPixel);

                    DisplayCalculatingPaths = true;
                    DisplayCalculatingROI = false;
                }

                else if (!DisplayCalculatingROI && DisplayCalculatingPaths)
                {
                    if (IsSimulation)
                    {
                        DisplayCalculatingPaths = true;
                        // This needs to be spun off to its own thread
                        CalculateCourierPaths();
                    }

                    SetupCorpsLists();

                    EndTurn = false;
                    Mouse.SetCursor(MouseCursor.Arrow);
                }
            } // EndTurn == true

Your thoughts on finishing this up would be greatly appreciated.

My bad, that syntax should be:

Task.Run(() =>
{
	// separate thread code here
});

Updated previous snippet as well.

Well, it runs but it doesn’t have the effect I was hoping for. In the debugger it trips the call to display text to the screen but it never displays it.

As well as threading you might consider co routines, you can write your own, i have a nuget package i wrote to manage mine.

Its not as good as threading imo, but if you are not use to writing thread safe code can be easier to implement.

You can find the source code to all my packages here

I dont have an explicit sample for coroutines, but if you look at the other packages (Scenes) its used in those.

Off the back of this might do one for old school threading and asynchronous methods too.

Your gane looks amazing, by the way, keep at it :slight_smile:

Thanks for the reply! What you’re seeing is only the ‘game engine’. There are four other programs that make up the game: the Army Editor, the Map Editor, the Scenario Editor and the launcher. You can recreate any battle during the ‘Age of Blackpowder’ (US Civil War, Napoleonics, etc.). Here’s a link to my development site: https://www.general-staff.com/

I’ve got a good buddy who is very experienced with writing asynchronous methods. That will probably be the solution.

In the meantime, I’m thinking of doing something that’s just plain wrong. All I want to do is put up some text like, “Calculating Fog of War…” I actually do exactly this on the opening splash screen:

Not knowing any better I put all this in Draw(). So, there’s a screen update in Draw() and then I call “CalculateFogOfWar()”. Obviously, nothing else can be drawn to the screen, but I don’t need anything else drawn to the screen at this time. So, I’ll give this a shot, and see if it works. And the fall back position is my buddy making the big number crunching methods asynchronous.

EDIT…
Yeah, it didn’t work. I’ve got 4 methods

ProcessTurn.ProcessEndTurn();
TacticalAILib.FogOfWar.CalculateFoW()
ROI.CalculateROI()
CalculateCourierPaths();

And as soon as they’re hit all Draw()ing stops. I guess I’ll just have to make them asynchronous. Sigh.

It doesn’t work because you need the draw call to finish so instead of calling the slow function you can tell it to draw the text “Calculating fog of war” and set a boollian like CalculateFogOfWarNextFrame and the next time through you can look at the boolian and do the slow function but the drawing of the text will have happened because the frame finished.

Without having access to your source code, and by my own experience with similar kind of games , I did this:
1-Separate the heavy task into another thread
2-Create a queue of messages to draw, so the heavy task thread pushes messages into the queue
3- The queue runs in the mean thread and displays messages as needed , so you don’t miss to show any
4- process queue in main thread and update using gametime as needed, while heavy task is running make sure you have a flag so the main game doesn’t allow the player to click or do things that should be waiting for.

I did similar thing for a real time strategy game I made with hundreds of a* path finding entities, when i was running in one thread I couldn’t have more than a few hundred units at the same time without frame drops and draw skipping, after that change I could have thousands of units without visible slowdown, though sometimes some units waited for quite a few seconds to move if I had more than 10000 units in play, but the user did not experience any slowdown during play.
It would be more useful to break your logic into many threads so you can make your game faster if the user has more cores in their computer.