Timers and countdowns
You should now have a level complete with geometry and coin objects. Thanks to our newly added Coin.cs script, the coins are both countable and collectible. Even so, the level still poses little or no challenge to the player because there's no way the level can be won or lost. Specifically, there's nothing for the player to achieve. This is why a time limit is important for the game: it defines a win and loss condition. Namely, collecting all coins before the timer expires results in a win condition and failing to achieve this results in a loss condition. Let's get started at creating a timer countdown for the level. To do this, create a new and empty game object by selecting GameObject | Create Empty and rename this LevelTimer. See Figure 2.25:
Next, create a new script file named Timer.cs and add it to the LevelTimer object in Scene. By doing this, the timer functionality will exist in the scene. Make sure, however, that the timer script is added to one object, and no more than one. Otherwise, there will effectively be multiple, competing timers in the same scene. You can always search a scene to find all components of a specified type by using the Hierarchy panel. To do this, click in the Hierarchy search box and type t:Timer. Then press Enter on the keyboard to confirm the search. This search the scene for all objects with a component attached of the timer type, and the results are displayed in the Hierarchy panel. Specifically, the Hierarchy panel is filtered to show only the matching objects. The t prefix in the search string indicates a search by type operation. See Figure 2.26:
You can easily cancel a search and return the Hierarchy panel back to its original state by clicking on the small cross icon aligned to the right-hand side of the search field. This button can be tricky to spot. See Figure 2.27:
The timer script itself must be coded if it's to be useful. The full source code for the Timer.cs file is given in the following Code Sample 2.6. This source code is highly important if you've never scripted in Unity before. It demonstrates so many critical features. See the comments for a fuller explanation.
//------------------------- using UnityEngine; using System.Collections; //------------------------- public class Timer : MonoBehaviour { //------------------------- //Maximum time to complete level (in seconds) public float MaxTime = 60f; //------------------------- //Countdown [SerializeField] private float CountDown = 0; //------------------------- // Use this for initialization void Start () { CountDown = MaxTime; } //------------------------- // Update is called once per frame void Update () { //Reduce time CountDown -= Time.deltaTime; //Restart level if time runs out if(CountDown <= 0) { //Reset coin count Coin.CoinCount=0; Application.LoadLevel(Application.loadedLevel); } } //------------------------- } //-------------------------
Code Sample 2.6
The following points summarize the code sample:
- In Unity, class variables declared as public (such as public float MaxTime) are displayed as editable fields in the Object Inspector of the editor. However, this applies to a range of supported data types only, but it's a highly useful feature. It means that developers can monitor and set public variables for classes directly from the Inspector as opposed to changing and recompiling code every time a change is needed. The private variables, in contrast, are hidden from the Inspector by default. However, you can force them to be visible, if needed, using the SerializeField attribute. The private variables prefixed with this attribute, such as the CountDown variable, will be displayed in the Object Inspector just like a public variable, even though the variable's scope still remains private.
- The Update function is a Unity native event supported for all classes derived from MonoBehaviour. Update is invoked automatically once per frame for all active GameObjects in the scene. This means that all active game objects are notified about frame change events. In short, Update is therefore called many times per second; the game FPS is a general indicator as to how many times each second. The actual number of calls will vary in practice from second to second. In any case, Update is especially useful to animate, update, and change objects over time. In the case of a CountDown class, it'll be useful to keep track of time as it passes, second by second. More information on the Update function can be found at the online Unity documentation at http://docs.unity3d.com/ScriptReference/MonoBehaviour.Update.html.
Note
In addition to the
Update
function called on each frame, Unity also supports two other related functions, namely,FixedUpdate
andLateUpdate
.FixedUpdate
is used when coding with Physics, as we'll see later, and is called a fixed number of times per frame.LateUpdate
is called once per frame for each active object, but theLateUpdate
call will always happen after every object has received an Update event. Thus, it happens after the Update cycle, making it a late update. There are reasons for this late update and we'll see them later in the book.More information on
FixedUpdate
can be found in the online Unity documentation at http://docs.unity3d.com/ScriptReference/MonoBehaviour.FixedUpdate.html More information on theLateUpdate
function can be found in the online Unity documentation at http://docs.unity3d.com/ScriptReference/MonoBehaviour.LateUpdate.html. - When scripting, the static Time.deltaTime variable is constantly available and updated automatically by Unity. It always describes the amount of time (in seconds) that has passed since the previous frame ended. For example, if your game has a frame rate of 2 FPS (a very low frame rate!) then deltaTime will be 0.5. This is because, in each second, there would be two frames, and thus each frame would be half a second. The deltaTime is useful because, if added over time, it tells you how much time in total has elapsed or passed since the game began. For this reason, deltaTime floating point variable is used heavily in the Update function for the timer to subtract the elapsed time from the countdown total. More information can be found on deltaTime at the online documentation at http://docs.unity3d.com/ScriptReference/Time-deltaTime.html.
- The static Application.LoadLevel function can be called anywhere in code to change the active scene at runtime. Thus, this function is useful to move the gamer from one level to another. It causes Unity to terminate the active scene, destroying all its contents, and load a new scene. It can also be used to restart the active scene, simply by loading the active level again. Application.LoadLevel is most appropriate for games with clearly defined levels that are separate from each other and have clearly defined beginnings and endings. It is not, however, suitable for large open-world games in which large sprawling environments stretch on, seemingly without any breakage or disconnection. More information on Application.LoadLevel can be found in the online Unity documentation at http://docs.unity3d.com/ScriptReference/Application.LoadLevel.html.
After the timer script is created, select the LevelTimer object in the scene. From the Object Inspector, you can set the maximum time (in seconds) that the player is allowed in order to complete the level. See Figure 2.28. I've set the total time to 60 seconds. This means that all coins must be completed within 60 seconds from the level start.
If the timer expires, the level is restarted.
Great work! You should now have a completed level with a countdown that works. You can collect coins and the timer can expire. Overall, the game is taking shape. There is a further problem, however, which we'll address next.