Friday 19 December 2014

Quick Tip: Pause, Slow Motion & Double-Time in Unity

Pausing a Unity game is easy. There are a couple of things you should be aware of, but we'll get to those at the end of this tip. We're going to add a few simple GUI buttons to control time within a basic Unity project
Step 1: Create a New Project
Let's start off by opening Unity and creating a new project. You may want to add some animation or some kind of action to the proceedings, so that you can actually see an effect when we press the buttons. In the example provided, I just made some simple spinning cubes.
Opening a new project is nice and simple. Once Unity has finished loading, if you have it set to show the Project Wizard you can click the 'Create New Project' tab and enter a filename. Unity will take care of the rest. If the Project Wizard window doesn't come up when you launch Unity, go to File > New Project.
Creating a new Unity project
Unity has its own GUI system built in, which goes far beyond this tip. Let's just say that everything GUI wise happens in a function called OnGUI and the GUI class provides a whole bunch of cool things, like a button.
Go ahead and create a new script through the Assets menu - select Assets > Create > JavaScript:
Creating a new script
Your new script should appear in the Project window. Click once on the script and it should highlight so that you can rename it. Name the script "PauseScript" and hit enter. Now we want to start coding, so go ahead and double-click it this time. That should open up the default script editor with Unity's template / default code already in it. Once the script editor is loaded up, delete the default code and replace it with the code below:
1
2
3
4
5
6
7
function OnGUI() {
    // show a 'pause' button
    if(GUI.Button(Rect(0,0,120,30),"TOGGLE PAUSE")){
        // call our toggle function
        doPauseToggle();
    }
}
GUI.Button requires a Rect (which contains the x,y,width and height of our button) and the string to display within it. Finding out whether or not the button has been pressed is as simple as putting our GUI.Button draw method into an if statement.
Here, we refer to a function that we are going to call doPauseToggle(). This will be a simple function that checks to see if we are paused or un-paused, then performs the correct action. Add the following code to the bottom of your PauseScript.js script, below the OnGUI function we added in Step 2:
01
02
03
04
05
06
07
08
09
10
function doPauseToggle() {
    // here we check to see if we are running at a time scale above 0
    if(Time.timeScale>0){
        // time scale is above zero, so we need to pause the game here
        pauseGame();
    } else {
        // time scale was less than zero, so we unpause the game here
        unPauseGame();
    }
}
Time.timeScale is how we deal with pausing the game. Essentially, we change the scale at which time is passing. When timeScale is 1.0, the time is realtime. At zero (0), time is paused. So in our doPauseToggle function we simply check to see if time is greater than zero, un-paused, if so we call a new pauseGame() function. Otherwise, unPauseGame() is called.
The functions in Step 3, pauseGame() and unPauseGame(), simply manipulate theTime.timeScale value to either 0 or 1. Add the following code to the bottom of your PauseScript.js script, below the code we added in Step 3:
1
2
3
4
5
6
7
function pauseGame () {
    // set scale at which time passes to 0, freezing time(!)
    Time.timeScale=0;
    }function unPauseGame () {
    // set scale at which time passes to 1, running at realtime again
    Time.timeScale=1;
}
Believe it or not, that's all we need to do to add a pause button to a Unity file. If you have some action in your Unity scene, you will now see that it pauses and un-pauses whenever we click the button! Note that if you don't have anything going on in your scene, you won't see anything happen when you hit play.
Are you thinking what I'm thinking? If 0 pauses and 1 makes it go, what about 0.5? Think bullet-time!
First, let's go ahead and add a new button to our OnGUI() function. Drop this at the bottom of the function, just above the closing } brace.
1
2
3
4
5
// show a 'slowmo' button
if(GUI.Button(Rect(0,30,80,30),"SLOWMO")){
    // call our toggle function
    slowMo();
}
Now we need a new function to adjust the time factor to less than 1. Scroll down to the bottom of your PauseScript.js script and add this new function:
1
2
3
4
function slowMo () {
    // set scale at which time 5asses to 0.1, running in slowmo
    Time.timeScale=0.1;
}
...thus proving that you don't have to be Neo for bullet-time in Unity!
Finally, for fun, let's make everything go a little crazy by playing in double time. Although I haven't seen this used in an actual game mechanic, I have seen time scaled temporarily to get a particle effect up and running – it was a fire effect and the author wanted the screen to be filled with fire quickly to bring the background of his menu screen to life. He scaled time up high for a second after the menu loaded and the fire filled the screen. He then reduced time back to 1 so that the flames moved correctly and everything else in the game was at regular speed.
To add double time, we'll follow the same procedure as in Step 5, adding a new button that will call a new function. The new function will simply set our timeScale to 2. Find the OnGUI() function in your PauseScript.js script and add the following, above the end of the function and above the closing } curly brace.
1
2
3
4
5
// show a 'double time' button
if(GUI.Button(Rect(0,60,120,30),"DOUBLE TIME")){
    // call our toggle function
    doubleTime();
}
Right. We have a button, let's add in that doubleTime() function:
1
2
3
4
function doubleTime () {
    // set scale at which time passes to 0.5, running in slowmo
    Time.timeScale=2;
}
For fun, try adjusting the timeScale up to 100 and see what happens.
Let's take stock of our script. Here's the PauseScript.js in full, just in case anything bad happened along the way:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function OnGUI() {
    // show a 'pause' button
    if(GUI.Button(Rect(0,0,120,30),"TOGGLE PAUSE")){
        // call our toggle function
        doPauseToggle();
    }
    // show a 'slowmo' button
    if(GUI.Button(Rect(0,30,80,30),"SLOWMO")){
        // call our toggle function
        slowMo();
    }
    // show a 'double time' button
    if(GUI.Button(Rect(0,60,120,30),"DOUBLE TIME")){
        // call our toggle function
        doubleTime();
    }
}
 
function doPauseToggle() {
    // here we check to see if we are running at a time scale above 0
    if(Time.timeScale>0){
        // time scale is above zero, so we need to pause the game here
        pauseGame();
    } else {
        // time scale was less than zero, so we unpause the game here
        unPauseGame();
    }
}
 
function pauseGame () {
    // set scale at which time passes to 0, freezing time(!)
    Time.timeScale=0;
}
 
function unPauseGame () {
    // set scale at which time passes to 1, running at realtime again
    Time.timeScale=1;
}
 
function slowMo () {
    // set scale at which time passes to 0.1, running in slowmo
    Time.timeScale=0.1;
}
 
function doubleTime () {
    // set scale at which time passes to 0.5, running in slowmo
    Time.timeScale=2;
}
It is important to note that Time.timeScale will not affect code within an Update()function. Update takes place every "tick" and it happens outside of time scale, so if you need things to happen as the game is paused, such as a background animation, the place to put it is most likely Update. Check the Unity documentation for more information on this.
Note that if you change the Time.timeScale value, any time-related actions such as calls to the Invoke function or any timers that use values from Time.time will also be affected by the change. If your game runs at twice the speed, you will have to halve the speed of your time-based actions (for example, if you have a racing game with a lap timer and your Time.timeScale is set to 2.0, you will need to slow down the lap timer by a half for it to accurately measure time).
Physics reactions are not directly affected, so your game should run as it normally would collisions and physics-wise - only on a different time scale.
That's it! Thanks for reading my quick tip. Play around with Time.timeScale and I look forward to seeing cool Matrix-like effects in your projects. Have fun, making games!

No comments:

Post a Comment