rds1983 / Myra

UI Library for MonoGame, FNA and Stride
MIT License
728 stars 94 forks source link

Bug: UI does not update on time #314

Open gamedevcb opened 3 years ago

gamedevcb commented 3 years ago

I am having a problem with something that I thought would be a common use case. Say the user clicks a button that would trigger a long running function, and I would like to produce some UI feedback before that function executes to let the user know that something is happening:

MyMenuItem.Selected += (s, a) =>
{
  _pleaseWaitScreen.Visible = true;
  LongRunningFunction();
};

What I would expect is that the '_pleaseWaitScreen' Panel will become visible first, and the 'LongRunningFunction()' executes after. Instead, The 'LongRunningFunction()' executes first, the game hangs, and then it makes the '_pleaseWaitScreen' visible after. I tried calling _desktop.Render() before the 'LongRunningFunction()' to force an update, but that did not help. I am on Myra 1.2.3 and MonoGame 3.8.0.1641.

gamedevcb commented 3 years ago

Hey @rds1983 I was wondering if you perhaps had an opportunity to look into this issue? Perhaps there is a workaround that I have missed? This would be much appreciated, thanks!

rds1983 commented 3 years ago

Hi @gamedevcb, Sorry for the late response. Try doing this:

  1. Add field bool _runLongFunction; to your class.
  2. Set it to true in that MyMenuItem.Selected handler.
  3. In your Game.Draw, after calling _desktop.Render, add code
    if (_runLongFunction) {
    LongRunningFunction();
    _runLongFunction = false;
    }

Probably this will work. However generally such problem is handled by running LongRunningFunction in the separate thread.

gamedevcb commented 3 years ago

I very much appreciate you looking into this @rds1983, however, unfortunately your suggestion hasn't worked. LongRunningFunction() is actually the main game loading function, thus, it shouldn't be on a separate thread (I may not even be able to use multi threading). The player just needs to see a notification that says 'Please wait while the game is loading', while his game is loaded from disk, and there is nothing else for him to do in the meanwhile, he just needs to know what he is waiting for.

The only thing I could think of now is to create a temporary timer to delay the LongRunningFunction(), but that looks like an ugly hack for something that should just work.

NinthDesertDude commented 9 months ago

You need one full cycle of the Draw call because Myra does everything exclusively in it.

Add this variable to your class: int loadAfterNFrames = 0;

When you set pleaseWaitScreen to be visible, also do: loadAfterNFrames = 2;

At the end of the draw step:

if (loadAfterNFrames > 0)
{
  loadAfterNFrames--;
  if (loadAfterNFrames == 0)
  {
    LongRunningFunction();
  }
}
NinthDesertDude commented 9 months ago

This bug is 2 years old and I gave it an answer just in case; I think it should probably be closed.