Daniel Lip
Daniel Lip

Reputation: 11317

How can I pause/resume a Coroutine when pausing the game with timeScale 0f?

For example this Coroutine. Let's say I have a ui button that pause the game :

Time.timeScale = 0f;

But timeScale is not working on Coroutines.

And if I have many Coroutines in the game is there a way to find all the Coroutines and then loop on them and pause them or do I need to add a pause loop to each Coroutine ?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.PostProcessing;

public class DepthOfField : MonoBehaviour
{
    public UnityEngine.GameObject player;
    public PostProcessingProfile postProcessingProfile;
    public bool dephOfFieldFinished = false;
    public LockSystem playerLockMode;

    private Animator playerAnimator;
    private float clipLength;
    private Coroutine depthOfFieldRoutineRef;

    void Start()
    {
        if (depthOfFieldRoutineRef != null)
        {
            StopCoroutine(depthOfFieldRoutineRef);
        }

        playerAnimator = player.GetComponent<Animator>();

        AnimationClip[] clips = playerAnimator.runtimeAnimatorController.animationClips;
        foreach (AnimationClip clip in clips)
        {
            clipLength = clip.length;
        }

        var depthOfField = postProcessingProfile.depthOfField.settings;
        depthOfField.focalLength = 300;
        StartCoroutine(changeValueOverTime(depthOfField.focalLength, 1, clipLength));
        postProcessingProfile.depthOfField.settings = depthOfField;
    }

    IEnumerator changeValueOverTime(float fromVal, float toVal, float duration)
    {
        playerLockMode.PlayerLockState(true, true);

        float counter = 0f;

        while (counter < duration)
        {
            var dof = postProcessingProfile.depthOfField.settings;

            if (Time.timeScale == 0)
                counter += Time.unscaledDeltaTime;
            else
                counter += Time.deltaTime;

            float val = Mathf.Lerp(fromVal, toVal, counter / duration);

            dof.focalLength = val;
            postProcessingProfile.depthOfField.settings = dof;

            yield return null;
        }

        playerAnimator.enabled = false;
        dephOfFieldFinished = true;
        depthOfFieldRoutineRef = null;
    }
}

Upvotes: 1

Views: 2269

Answers (1)

Samuel Goldenbaum
Samuel Goldenbaum

Reputation: 18919

You could use the Observer Pattern and create a set of states for the game like: LoadingGame, Paused, LoadingLevel etc and use events to hook into state changes and take appropriate action.

For example:

 public enum GameState {
        LoadingGame,
        LoadingLevel,
        Paused,
        ...
    }

Then in a global manager, fire off an event when the state changes:

   public event Action<GameState, GameState> OnGameStateChange;

   public void SwitchGameState (GameState to) {
        previousState = CurrentState;
        CurrentState = to;

        switch (to) {
            case GameState.Paused:
                ...
                break;
            ...
        }

        OnGameStateChange?.Invoke (previousState, CurrentState);
    }

Any other components, like your DepthOfField can listen to the event and take appropriate action:

    void Awake () {
        gameManager.OnGameStateChange += HandleGameStateChange;
    }

    void HandleGameStateChange (GameState from, GameState to) {
        switch (to) {
            case GameState.Paused:
                // stop or pause couroutine etc
                StartCoroutine(coroutine);
                break;
        }
    }

Upvotes: 3

Related Questions