Home > Mobile >  Sometimes wait for Coroutine to complete in Unity
Sometimes wait for Coroutine to complete in Unity

Time:05-17

I'm working on a system to save game state in Unity. The current structure looks like this:

public void SaveGame() {
    StopAllCoroutines();
    if (source != null)
    {
        source.Cancel();
    }
    StartCoroutine(DoSave());
}

protected IEnumerator DoSave(){
    /// Code that prepares a bunch of stuff necessary to save the game goes here
    foreach (item in itemsToSave)
    {
        // Code that adds 'item' to a data structure in memory that is accessible by later functions goes here //
        yield return new WaitForSeconds(0.05f);
    }
    
    source = new CancellationTokenSource();   
    WriteFiles(source.Token);
}

private async Task WriteFiles(CancellationToken token)
{
    Task task = Task.Run(() => GenerateSaveFiles(), token);
    // (A canceled task will raise an exception when awaited).
    await task;
}

private void GenerateSaveFiles()
{
    /// Functions that actually write to disk go here 
    /// E.g. File.WriteAllText();
    /// This function has access to the place in memory where 'items' were being added to in the DoSave() foreach loop, so it just dumps that memory storage to disk
}

I set it up this way so I can save asynchronously in a separate thread and track the status of it (e.g. if it fails or not, and error messages).

SaveGame() is then called around my code base whenever different events (like picking up items, killing enemies etc) to save the game in real-time. This mostly works well for 99% of my use-cases but there are instances where I want to call SavGame() and wait for it to finish before doing anything else at all. For example, if a player dies, I want to write the state to disk without giving a chance for anything else (e.g. respawning the player in a different scene) to occur.

I have looked into a few things like callbacks (e.g. https://codesaying.com/action-callback-in-unity/) but I'm not sure how to correctly set this up for my use-case of mostly having SaveGame() tasks run async but other times being able to wait for it to complete, or at a minimum having the method that called SaveGame() knowing when it has completed.

CodePudding user response:

Just do a simple Boolean property that anyone can access:

public bool IsSavingGame { 
    get; private set; 
}

// ...

private IEnumerator SaveGameCoroutine(){
    IsSavingGame = true;

    // Your save functionality.

    IsSavingGame = false;
}

For example, if you want deny any loading of scene while it's saving, you can write up this script:

// or you can singleton it
[SerializeField]
private SaveManager yourSaveManger

public void LoadScene(){
    StartCoroutine(LoadSceneCoroutine());
}

private IEnumerator LoadSceneCoroutine(){
    while (yourSaveManger.IsSavingGame){
        // Show a loading or saving screen or something
        yield return null;
    }

    SceneManager.LoadScene("Your Scene Name");
}

CodePudding user response:

You can just wait the task to complete.

protected IEnumerator DoSaveAndWait(){
    ...
    WriteFiles(source.Token).Wait();
    //files are written at this line
}
  • Related