1
\$\begingroup\$

I have implemented serialization/de-serialization process in my Unity game where I save/load data from/to XML file.

During the serialization, my game isn't responding until the serialization is finished and this is OK.

What is wrong is that before I call the serialization/de-serialization, I want to show saving/loading panel and show log process, but can't: this panel and all the log information show up only after the serialization/de-serialization is finished.

What I have verified and tried:

  1. The panels hierarchy is correct
  2. During serialization/de-serialization, no error occur
  3. Panels are assigned manually to specified variables
  4. If I comment out the save/load calls and leave only the panel, then the panel is shown
  5. I have also tried to put the thread to sleep but still, nothing happens.

Can anybody can help me? Because I really don't know why this is happening like this.

Here is an snippet of my code:

public void SaveGameClick() {
  SavingPanel.gameObject.SetActive(true); // if I leave only this line and the other line 
                                          // commenting the panel shows perfectly
  Debug.Log("Saving..."); // this Saving word shown only when the _savegame.Save() method 
                          // finished
  if(_saveGame.Save("save.xml", Seed)) {
    Debug.Log("Saved");
    CyberTeamManagerSubMenu.gameObject.SetActive(false);
    _isShowCyberTeamManagerSubMenu = false;
    SavingPanel.gameObject.SetActive(false); 
  } else {
    Debug.Log("Save Error");
    SavingPanel.gameObject.SetActive(false);
  }
}
\$\endgroup\$
1
\$\begingroup\$

Unity runs most scripts single-threaded. That means the same thread that's running your script is doing your rendering.

So, a new frame isn't rendered until the engine finishes running every script - including your saving script.

In this case, your first line marks the saving panel to be visible in the next rendered frame:

SavingPanel.gameObject.SetActive(true);

But then you start serializing immediately. Since we haven't returned from this click function, the engine hasn't had a chance to draw that frame before it hits this line:

if(_saveGame.Save("save.xml", Seed))

What you can do instead is to queue-up your saving, so it starts after the frame has been rendered, or on the start of the next frame's update:

public void SaveGameClick() {
  SavingPanel.gameObject.SetActive(true);

  StartCoroutine(QueuedSave());
}

IEnumerator QueuedSave() {
  // Let the game display a frame before we try to save.
  yield return new WaitForEndOfFrame();

  // Now we're ready to save.
  if(_saveGame.Save("save.xml", Seed)) {
    CyberTeamManagerSubMenu.gameObject.SetActive(false);
    _isShowCyberTeamManagerSubMenu = false;        
  } else {
    Debug.Log("Save Error");
  }

  SavingPanel.gameObject.SetActive(false); 
}

Note that this method still won't let you show incremental save progress, or even animate a spinner while the game saves. We're still locking up the main thread and preventing new frames from rendering for the whole save process, we're just starting it one frame later now.

If you want to ensure that your game can remain responsive during heavy work like this, you'll want to either...

  • break your saving coroutine into bite-sized pieces or a loop that can yield back to the main thread periodically so more frames can be drawn, or...

  • start a separate thread to do the saving (just make sure that nothing important is able to change state during the save, or your saved data might end up inconsistent).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.