Search Unity

Pause ~ A Pausable Behaviour

Discussion in 'Scripting' started by PizzaPie, Apr 20, 2018.

  1. PizzaPie

    PizzaPie

    Joined:
    Oct 11, 2015
    Posts:
    106
    After messing around to find a decent way to pause Update function without the obvious

    Code (csharp):
    1.  void Update() { if(Manger.Instance.IsPaused) return; //...}
    or disable the behaviour

    I ve ended up with this->
    First is the PauseController (unfortunately i am setting it as Singleton for the main reason that there should be only one instance of it and to ease up references)

    Code (CSharp):
    1.  
    2.     using UnityEngine;
    3.     using System;
    4.  
    5.     public class PauseController : MonoBehaviour {
    6.  
    7.         public static PauseController Instance;
    8.  
    9.         private bool isPaused;
    10.         private event EventHandler<PauseEventArgs> Pause;
    11.  
    12.         private void Awake()
    13.         {
    14.             Instance = this;    //add singleton Code! or use the Singleton<> from Unity
    15.                                 //Scene persistance is not really needed
    16.         }
    17.  
    18.         private void Update()
    19.         {
    20.             if (Input.GetKeyDown(KeyCode.Return))
    21.             {
    22.                 isPaused = !isPaused;
    23.                 OnPause(new PauseEventArgs(isPaused));
    24.             }
    25.         }
    26.  
    27.         protected virtual void OnPause(PauseEventArgs e)
    28.         {
    29.             if (Pause != null)
    30.                 Pause(this, e);
    31.         }
    32.  
    33.         public void SubscribeOnPause(EventHandler<PauseEventArgs> handler)
    34.         {
    35.             Pause += handler;
    36.         }
    37.  
    38.         public void UnSubscribeOnPause(EventHandler<PauseEventArgs> handler)
    39.         {
    40.             Pause -= handler;
    41.         }
    42.     }
    Second is the PausableBehaviour this should be the base class for all Behaviours you want to have pausable update.

    Code (CSharp):
    1. public abstract class PausableBehaviour : MonoBehaviour {
    2.  
    3.         private bool isPaused = false;
    4.  
    5.         protected virtual void Start()
    6.         {
    7.             PauseController.Instance.SubscribeOnPause(OnPauseHandler);
    8.         }
    9.  
    10.         protected virtual void OnDestroy()
    11.         {
    12.             PauseController.Instance.UnSubscribeOnPause(OnPauseHandler);
    13.         }
    14.  
    15.         protected void Update()
    16.         {
    17.             if (isPaused)
    18.                 return;
    19.             PausableUpdate();
    20.         }
    21.  
    22.         //Make sure to implement this on Child class
    23.         protected virtual void PausableUpdate()
    24.         {
    25.             Debug.Log("Base Update!");
    26.         }
    27.  
    28.         protected virtual void OnPauseHandler(object sender, PauseEventArgs e)
    29.         {
    30.             isPaused = e.IsPause;
    31.         }
    32.     }

    And lastly an example of a class inheriting the behaviour of PausebleBehaviour


    Code (CSharp):
    1. public class SomePausableBehaviour : PausableBehaviour {
    2.  
    3.         protected override void Start()
    4.         {
    5.             base.Start();
    6.             //Do stuff!
    7.         }
    8.  
    9.         protected override void PausableUpdate()
    10.         {
    11.             base.PausableUpdate();  //Do not delete this
    12.             //your Update code
    13.         }
    14.  
    15.         protected override void OnPauseHandler(object sender, PauseEventArgs e)
    16.         {
    17.             base.OnPauseHandler(sender, e); //Do not delete this
    18.  
    19.             //your code here, invoked when state is changed
    20.             //use e.IsPaused to pick up current state
    21.         }
    22.     }
    Make sure to use *protected override void PausableUpdate()* and do **not** override *Update()* on child objects.


    -Why not pick up the state directly from PauseController or from a static bool isPaused?
    -Mainly because you break several OOP principals (which the Singleton already does) but the damage is contained. Also you get an event each time the state changes so you can accordingly manipulate other components and parts based on supplied state.

    -Why you do not use two events with no EventArguments (OnPause / OnUnPause) and a local bool to hold the state?
    -Because you no longer have a global state it is more of local state, which currently exists but can not be manipulated from child.

    -Can i use the same function declared on PausableBehaviour?
    -Yes all of them **Except** Update() which should not get ovewritten do use instead **PausableUpdate()**, make sure to call .base for all of them though.

    -Why not use an Observer-Observable pattern?
    -Got nothing on this, it would be probably better.

    -What happens if I instantiate a new Object while isPaused = true;
    -Well the code does not account for this senario and the Update() of the new Object will be called normaly thus creating unwanted behaviour. As a principal i see no reason why would you Instantiate anything while on Pause. Now if it is needed, you need to update its internal state accordingly to do this you may add a property on the Controller -> public bool IsPaused {get {return isPaused;}} and call it inside the abstract class inside the Start(). Bit hacky though and breaks the whole consept.

    -Could I use Interfaces instead of Inheritance
    -Yes but there would be a lot of copy pasting

    Would love to hear thoughts and suggestions on this. Possible bugs and fixes!
    Cheers!