- Home /
properly handling pause functionality with javascript/unityscript?
So within my game I have a clock script, which keeps track of it's own float based off of Time.time. You can manipulate it's verion of Time.time,you can pause time, count down time whatever you would like.
There is also a boolean ("if paused") attached to this clock script.
Up until this point, I've been handling this wrong and basically on everything that I need to be pausable I have it get the clock script, and in function Update, check the status on every frame if pause has happened.
pause = Clock.GetComponent(clockScript).paused
if (pause)
{
yadda yadda yadda...
}
I was talking to someone last night who's more experienced at code suggest to me to have whatever button or action that pauses the game, to basically do a check of all units currently in game that can be paused and then perform a co routine (function) to pause them, and of course the same to unpause.
So I "think" what he's suggesting is...
Funtion PauseEverything ()
{
Object[] objects = FindObjectsOfType (any object I want paused)
Run a Pause Function that each object that you can pause would have in it's script.
}
This sounds great but how do I get multiple scripts with multiple names to call out the same function? And the other worry I have is that I heard "FindObjects" or "GameObjects.Find..." is a very slow process and should be used sparingly especially during gameplay. I'm not sure if this all makes sense, I've only been coding for about 5 months now so I'm still learning lots. Of course any help on clearing this up would be appreciated.
Thats Javascript, not Java.
Two VERY different languages.
(And actually its not really Javascript either but UnityScript. Both Javascript and UnityScript derive from EC$$anonymous$$AScript but there are some important differences between the two. However most people in the unity community call it Javascript due to Unity referring to it that way.)
Answer by HappyMoo · Jan 21, 2014 at 10:19 PM
You don't need to implement your own Time that you can stop.
That's what timeScale is for. And you don't need to tell every Object that the game paused, because they will move based on deltaTime, which means they won't move anymore and on timeScale=0 FixedUpdate isn't called anymore.
It's my understanding (And I could very well be wrong here) but timeScale = 0 globally stops time. $$anonymous$$eaning everything now stops. Even things like the UI or various other units or abilities or functions that I want you to access or have move, are all frozen as well. Which is why I've created my own time, and now have all units I want paused access that variable.
As long as your functions don't make use of Time.deltaTime then I think you're okay. I don't think that the GUI makes use of Time.deltaTime either as referenced in Time.deltaTime's description:
Note that you should not rely on Time.deltaTime from inside OnGUI since OnGUI can be called multiple times per frame
Allot of functions and features in NGUI use time.Time and time.DeltaTime. As well, I'm running smooth moves which also uses time.Time on animation playback. So characters and NGUI are both frozen in this instance should I globally timeScale to 0.
if that's true, NGUI is using the system in a wrong way.
Also what nanobrain says. OnGUI is called multiple times per frame.
If I wanted to animate my GUI, I would use Time.realtimeSinceStartup to base my animations on
Answer by Nanobrain · Jan 21, 2014 at 06:50 PM
What about wrapping the pausable objects inside an empty gameobject. Then, when the game is paused, by whatever method, you can then use the BroadcastMessage method of your empty gameobject to call the pause method of it's children.
myEmptyGameObject.BroadcastMessage("Pause", null);
Pause objects could include and are not limited too bullets and enemies and player units, all which are instantiated as the game progresses.
Unfortunately, I think I'm stuck using GameObject.Find. I 'think' Find by .tag is probably the quickest?
For my purposes, I use this class to register pausable scripts (C#):
using UnityEngine;
using System.Collections;
//delegate with function signature to match update methods to be replaced
public delegate void EventHandler();
public class PauseController : $$anonymous$$onoBehaviour {
//the three events to track for a pausable object
private static event EventHandler PausableUpdate; //normal every-frame updating when not paused
private static event EventHandler PausableUpdate_OnPause; //called the first frame this gets paused
private static event EventHandler PausableUpdate_OnUnpause; //called the first frame this gets unpaused
//two variables needed for tracking pausing
private static bool isPaused = false;
private static bool wasPaused = false;
private void Update () {
//manual pause toggle in this class if desired (uncomment the next line to enable)
if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.P)) isPaused = !isPaused;
//if game was just paused, invoke all PausableUpdate_OnPauses...
if ((!wasPaused) && isPaused) {
wasPaused = true;
PausableUpdate_OnPause.Invoke();
}
//if game was just unpaused, invoke all PausableUpdate_OnUnpauses...
else if (wasPaused && (!isPaused)) {
wasPaused = false;
PausableUpdate_OnUnpause.Invoke();
}
//if game isn't paused, invoke all PausableUpdates
if (!isPaused) {
PausableUpdate.Invoke();
}
}
/// <summary>
/// For objects that need to save and load state just before and after a pause
/// (eg GameObjects with a Rigidbody component)
/// </summary>
/// <param name='methodToCallOnUpdate'>
/// $$anonymous$$ethod to call on update.
/// </param>
/// <param name='methodToCallOnPause'>
/// $$anonymous$$ethod to call on pause.
/// </param>
/// <param name='methodToCallOnUnpause'>
/// $$anonymous$$ethod to call on unpause.
/// </param>
public static void RegisterUpdate(EventHandler methodToCallOnUpdate, EventHandler methodToCallOnPause, EventHandler methodToCallOnUnpause) {
PausableUpdate += methodToCallOnUpdate;
PausableUpdate_OnPause += methodToCallOnPause;
PausableUpdate_OnUnpause += methodToCallOnUnpause;
}
/// <summary>
/// For scripts that don't need to save and load state just before and after a pause.
/// (should be most scripts)
/// </summary>
/// <param name='methodToCallOnUpdate'>
/// $$anonymous$$ethod to call on update.
/// </param>
public static void RegisterUpdate(EventHandler methodToCallOnUpdate) {
PausableUpdate += methodToCallOnUpdate;
}
//used for pausing, called by writing PauseController.Pause();
public static void Pause() {
isPaused = true;
}
//used for unpausing, called by writing PauseController.Unpause();
public static void Unpause() {
isPaused = false;
}
//used for checking if the game is paused, called by writing PauseController.IsPaused();
public static bool IsPaused() {
return isPaused;
}
}
(continued next post)
Answer by nesis · Jan 22, 2014 at 02:24 PM
Then, using the PausableObject class, I can register some methods in another script to be called just like they're the normal Update() method, with the benefit of being pausable.
For example, a script that constantly rotates an object:
using UnityEngine;
using System.Collections;
public class ConstantRotate : MonoBehaviour {
private bool oldIsSleeping = false;
private Vector3 oldVelocity;
private Vector3 oldAngularMomentum;
public void Start() {
//Register some methods from this script to be called by PauseController
//IMPORTANT: Do NOT use Update(), FixedUpdate(), or any of the other
//default update methods... If you do, during each frame, they'll get
//called twice - once by PauseController, then once by Unity! Write your
//own method names, but other than that pretend they're the same as
//the default update methods.
PauseController.RegisterUpdate(MyUpdate,OnPause,OnUnpause);
//For a script without a rigidbody attached to its GameObject, you can
//just use:
//PauseController.RegisterUpdate(MyUpdate);
}
public void OnPause() {
if (!rigidbody) return;
if (rigidbody.IsSleeping()) oldIsSleeping = true;
else {
oldVelocity = rigidbody.velocity;
oldAngularMomentum = rigidbody.angularVelocity;
rigidbody.Sleep();
oldIsSleeping = false;
}
}
public void OnUnpause() {
if (!rigidbody) return;
if (!oldIsSleeping) {
rigidbody.WakeUp();
rigidbody.velocity = oldVelocity;
rigidbody.angularVelocity = oldAngularMomentum;
}
}
public void MyUpdate() {
transform.rotation = transform.rotation * Quaternion.Euler(0f,10f*Time.deltaTime,0f);
}
}
Note that rigidbodies need special treatment during pausing / unpausing. This is something I hope the Unity devs will fix in the future, but for now this workaround using OnPause / OnUnpause methods works just fine (as long as nothing bumps into the rigidbody during the pause, of course - that'd make it wake up, and potentially start moving again... so to handle that, you'd need to destroy the rigidbody during OnPause then re-create it during OnUnpause with all its original properties saved and re-loaded).
Thanks nesis, it'll take me a bit to take a look at all this because I'm not well versed in C# only javascript. For your rigid bodies problem, couldn't you just enable/disable collision or the triggers?
If you want non-pausable things to collide with those rigidbody colliders during a pause, then disabling the collider isn't a viable solution.
The ideal scenario (that Unity doesn't currently offer) would be to disable a rigidbody, save its member variables somewhere, and keep its collider enabled so things can bump into it (but not knock it around since it wouldn't have its rigidbody enabled). Then when re-enabling the rigidbody, set its member variables to what they were before it was disabled.
Really what you do with the paused rigidbodies depends on what you reasonably expect to happen to them while they're paused.
I'm still thinking about a cleaner way to do this.
Couldn't you just do the array method? Have each and any object that can be paused operate under a boolean;
if (!paused)
{
Do what I'm supposed to do;
}
Then have your master script create an array the moment the player hits pause
pausedObjects [] = GameObjects.FindByTag ("objects I want paused");
pausedObjects.Broadcast$$anonymous$$essage ("pause = true");
I'm sure I've messed up the syntax as I've never broadcast a message, but from my understanding, it sends a message to any object in an array regardless of script name?
If this method could work, how would I populate an array with FindByTag, but search for multiple tags?
Could you help me with the proper syntax Nano? I think I messed up a few things...
someGameObject.Broadcast$$anonymous$$essage("some$$anonymous$$ethod") calls a method called some$$anonymous$$ethod() in any script attached to someGameObject. I don't think there's a way to find multiple objects by multiple tags in one method call, so you should make an array of tag strings, then iterate over that, collecting all objects in a list.
Your answer
Follow this Question
Related Questions
Detect when user minimizes application 3 Answers
A Proper Way To Pause A Game 9 Answers
I am creating a pause button and when I click it the score keeps rising. How can I stop it? 1 Answer
OnMouseDown not firing 2 Answers
show pause menu issues 1 Answer