To begin with the user interface for our game, we need a class that manages when and how to use UI elements, such as buttons and when to show the UI screen.
In order for our other code classes to get a reference to the GameInfo
class, we can create a GameObject
in the scene that holds a GameInfo script component. To do this, perform the following steps:
Assets/Scripts
folder, right-click and select Create and then click on C# Script. Name this GameInfo
.GameInfo
script and select the Start
function as Awake
. Save this file and go back to Unity.GameInfo
.We now need to import some PNG files to represent elements of the UI. These PNG images will represent elements such as buttons. To do this, perform the following steps:
Assets
folder, right-click and select Create and then select Folder. Name this folder UI
.UI
folder and select Import New Asset…. Navigate to the book Art
files and find the folder named ChapterFive_UI
. Import the PNG files to the UI
folder.256
.Right-click on the Hierarchy tab. Then, click on UI and select Canvas. Name this new GameObject GameUI
.
With the GameUI GameObject selected, take a look at Inspector, Then, in the Canvas Scaler component, select UI Scale Mode as Scale With Screen Size. Also, select Reference Resolution as X1024 Y768. This will keep the UI in the same ratio, irrespective of the screen size of the device used, such as iPhone versus iPad.
Right-click on the GameUI GameObject. Then, click on
UI and select Image. Name this image CoinBackground
.
In the image component of the CoinBackground GameObject, click on the small circle next to the object field named Source Image. Search for ButtonBackground
and select, ButtonBackground.
With the CoinBackground GameObject selected, as Inspector, change the following settings:
-87.4
.-103.2
.0
.164
.64
.In the top-left corner of the Inspector tab, there is a box with red lines for two of its sides. Click on it and select the top right box selection:
This will anchor the image to that side of the UI, making a better reference for it to draw if the screen size is to change. To make it simpler, no matter the screen size, the image will be rendered to the same location of the screen space.
With the CoinBackground GameObject selected, right-click on it and select UI. Then, select Text from the list and name it CoinValue
.
Change the CoinValue text settings as follows:
19.6
.1.3
.0
.103.6
.41.1
.0
.With the CoinValue GameObject selected, right-click on it and select UI. Select Image from the list. Name this image CoinImage
.
Change the CoinImage settings as follows:
-73.5
.0
.0
.100
.100
.Pickup_Coin_0
.You should now see something similar to the following image in the top-right corner of the Game tab of the editor:
Before we set up the code to adjust the value in the CoinValue text component, we will set up the DistanceBackground field.
DistanceBackground
will reveal the distance the player has run for each attempt and will reset it to
zero if they restart.
With the GameUI GameObject selected, right-click on it and select UI. In the list, select Image. Name this image DistanceBackground
.
With the DistanceBackground GameObject selected, search for Inventory
and change its settings as follows:
-87.39999
-37
0
164
64
ButtonBackground
Still, with the DistanceBackground GameObject selected, right-click on it and select UI. Select Text from the list and name it DistanceValue
.
Change the DistanceValue settings using Inspector as follows:
0
-0.1
0
141
41.6
0
You should now see both the distance and the coin UI elements in the top-right corner of the Game tab in the editor if you play the game.
Having a pause button is a nice luxury for any game. Unity makes this functionality fairly simple.
Select the GameUI GameObject and right-click on it. Navigate to UI and select Button from the list. Name this button PauseButton
. Select the child GameObject named Text and delete it. We do not need text for PauseButton
.
With the PauseButton GameObject selected in Inspector, change its settings as follows:
34.5
-34
0
54
54
PauseButton
For PauseButton to work, we need to update our GameInfo
C# class. In the Assets/Scripts
folder, double-click on the GameInfo
file and add the following function in it:
// Toggle pause, on or off public void PauseGame( ) { Time.timeScale = ( Time.timeScale == 1 ) ? 0 : 1; }
This is a very simple function. We can set Time.timeScale
to zero or the one based on whether the current time scale is equal to one. If it is, we set it to zero. If it is not, set it to one; we will set it to one.
Save the GameInfo
file and go back to Unity.
In order for the PauseButton
to work, we need to create a button component and then set the OnClick
function of the button component to call our PauseGame
function.
Right-click on Hierarchy and select Create Empty. Name this new GameObject GameInfo
. Now, in Inspector, click on New Component. Then, search for GameInfo
and select GameInfo.
Select the PauseButton GameObject in Hierarchy. Then, in the Button script component in Inspector, click on the small plus symbol at the bottom of the Button script component. Then, click and drag the GameInfo GameObject onto the On Click () object field.
Now, click on the drop-down menu that says No Function and navigate to the selection called GameInfo. In the list for GameInfo, select PauseGame():
If you play the game now, when you click on PauseButton, the game will stop moving most things. The Obstacle
GameObjects will not stop rotating; we can fix this by alternating the Update
function in the Obstacle
class.
Open the Assets/Scripts
folder and double-click on the Obstacle
class. Change the Update
function as follows:
// Update is called once per frame void Update () { if (Time.timeScale == 1) { Pivot.transform.Rotate(Vector3.forward, RotationSpeed); } }
Now, PauseButton will freeze some aspects of the game, including the physics, the FixedUpdate
function, and the WaitForSecond
class. This means that as we are checking against Time.timeScale
, before rotating the axe(s) around, it will only rotate if Time.timeScale
is set to one. For example, if timeScale
is set to one, which means that the game is running at normal speed, the axes(s) can rotate. Otherwise, if timeScale
is zero, the axe(s) will stay stationary.
For the UI to see these values, we need to have a couple of our code classes to tell it what to display.
In the Assets/Scripts
file, open the Character
file under the line that uses the following code:
[System.NonSerialized] public int CoinCount;
Add:
public Canvas GameUI; [System.NonSerialized] public int DistanceCount; [System.NonSerialized] public float CurrentTime;
GameUI
: This will reference our Game User Interface Canvas GameObject.DistanceCount
: This will represent the distance that the character has moved across the screen so that we can display it to the screen as well as submit any leaderboard records related to the distance traveled. It is not based on the actual movement, rather we will use time to add to it.CurrentTime
: This will be the value we use to add to DistanceCount
.We also need to add another using
tag for the UI to have a reference to the Text class at the top of the Character
code file under the following code:
using UnityEngine; using System.Collections;
Now, add the following code:
using UnityEngine.UI;
In the same Character
class, navigate to the Update
function and under the lines of the following code:
// Update is called once per frame void Update () { Vector3 lockXPosition = transform.position; lockXPosition.x = 2.25f; transform.position = lockXPosition;
Add the following code:
if (!isDead && Time.timeScale == 1) { CurrentTime += Time.deltaTime + 0.025f; if (CurrentTime >= 1.0f) { AddDistance(1); CurrentTime = 0.0f; } }
This will make sure that the player is alive by making sure bDead
is false and also make sure that timeScale
is set to one, meaning the game is not paused before we change the CurrentTime
and CurrentDistance
values.
If these values can be changed (the condition is true
), we can add time.DeltaTime + 0.025f
to the CurrentTime
value. The 0.025f
parameter is a small addition to make the CurrentDistance
count faster than every one second.
As soon as CurrentTime
becomes greater than one, we can call the AddDistance
function and give it a value of one. We can also reset CurrentTime
back to zero so that it can begin to count up to one again.
Let's add the AddDistance
function to the Character
class. At the bottom of the class, under the ReviveCharacter
function, add the following code:
// Adds to the Distance count and updates the UI public void AddDistance(int Additional) { DistanceCount += Additional; if (GameUI != null) { Text distanceText = GameUI.transform.Find( "DistanceBackground/DistanceValue" ).GetComponent<Text>( ); if (distanceText != null) { distanceText.text = DistanceCount.ToString( ); } } }
We start this function by adding the value that is passed in to Additional
.
We then check to make sure that the reference to GameUI is not null, making sure GameUI has a reference before using it.
When we have a good reference to the GameUI, we can create a Text
local variable and assign it to the DistanceValue GameObject text component. This is done by using the GameUI reference and using its transform.Find
function to go through the hierarchy of the GameUI GameObject and look for the name of the GameObject passed into the Find
function. In our case, DistanceBackground
/ DistanceValue
, meaning it goes to DistanceBackground
and then looks for DistanceValue
in its children. When it gets there, we can use
GetComponent
to have the Text
component from the DistanceValue GameObject.
We then check whether the Text
reference we got from DistanceValue is valid. If it is valid, we can update the text of distanceText
to the DistanceCount
value. We can do this by using the ToString
function to convert the DistanceCount
integer value into a string so that we can assign it to the distanceText.text
value.
To break this down, we will add the distance, check for a good reference to the GameUI, get the Text component from one of its children and then change it to the value of DistanceCount, showing the player how far they have moved.
We also need to write a function called AddCoins
for the coins. In the Character
class, write the following function under the AddDistance
function:
// Add to the coin count public void AddCoins(int Additional) { CoinCount += Additional; if (GameUI != null) { Text coinText = GameUI.transform.Find( "CoinBackground/CoinValue" ).GetComponent<Text>( ); if (coinText != null) { coinText.text = CoinCount.ToString( ); } } }
This takes the exact same steps as the AddDistance
function. We can add CoinCount
, check for the valid GameUI reference and then update its child Text
component to change the text so that the player knows that the CoinCount
has changed.
The final step to having the CoinCount
change when a coin is collected is to have the Coin class tell the character that a coin has been picked up.
In the Assets/Scripts
folder, open the Coin
script file by double-clicking on it.
Navigate to the
OnTriggerEnter2D
function and change the following lines:
gameChar.CoinCount += 1; ActivateCoin( false );
To the following code:
gameChar.AddCoins(1); ActivateCoin( false );
As we are now adjusting the GameUI to show the CoinCount
on the screen, we made the AddCoins
function in the Character
class to do it, we no longer need to directly alter the CoinCount
for the Character
class in the Coin
. We can instead select the value we want to change it using AddCoins
.
This way, we can handle everything we need from the Character
class instead of relying on Coin
to directly alter the values in Character
.
Save the Coin
file and go back to Unity. If you play the game now, you should see the distance and coins adding up during the events that would add them up, running forward, or by collecting coins.
In order to complete our game loop, we need to allow the player to restart playing. As of now, as soon as the player character dies, they cannot keep playing because they will lose all control of the game. The restart button will allow them to restart playing and collect those glorious coins.
The restart button needs to do a couple of things. The major one is resetting the character so that it can run, collect coins again, and die as well. The next thing is resetting the level pieces so that the player has the same experience as the first attempt.
We also don't want the restart button to show that the player is alive only when the character has died.
Right-click on the GameUI GameObject and navigate to UI, select Button from the list, and name it RestartButton
.
With the RestartButton selected, in the Inspector tab, change the settings as follows:
0
93
0
128
64
RestartButton
Select the Text child GameObject of the RestartButton, search for its Inspector, and change its settings as follows:
We need to create a GameObject that will hold a GUITexture
component so that we can use it to turn the screen from completely black to completely transparent and vice versa, simulating a camera fade.
In Hierarchy, right-click and choose Create Empty. Name this new GameObject FadeObject
.
With the FadeObject selected, search for the Inspector window and click on Add Component. Search for GUITexture
and then select GUITexture.
Change its settings as follows:
-5
-5
0
0
—red, 0
—blue, 0
—green, 255
—alphaWe will move the
FadeObject
position in the world by negative five for X and Y just so that the black square doesn't get in the way of our scene. We will position it to the correct location when the game starts.
We now need to set up the GameInfo
class to manage our RestartButton, which includes a fading screen and communicates with the LevelPieceManager
and Character
class so that they know when to restart and what to do when they restart.
In the Assets/Scripts
folder, double-click on the GameInfo
script file. Add the following code at the top of the class:
// Reference to our Character public Character GameCharacter; // Reference to our levelPieceManager public LevelPieceManager LevelManager; // Fade object public Transform FadeObject; // Reference to fade GUITexture private GUITexture FadeTexture; // Is game restarting private bool bRestartingGame; // Is fading active private bool bStartFading; // Level and Character need resetting private bool bLevelAndCharacterRestart;
GameCharacter
: This will hold a reference to our Character
class in the level.LevelManager
: This will hold a reference to the LevelPieceManager
we have in our level.FadeObject
: This will hold the fade texture so that we can fade our game screen from clear to black and from black to clear.FadeTexture
: This is the GUITexture
component of FadeObject
, which is what we will be changing from black to clear.bRestartingGame
: This will be used to tell GameInfo
whether the game will restart.bStartFading
: This will be used to tell GameInfo
to start fading the screen.bLevelAndCharacterRestart
: This will be used to prevent the LevelPieces
and the Character
from continually being reset in the Update
function as we transition from the completely black to the transparent fade. This is because we reset the Character
and LevelPieces
before the fade is completely done.Now save the
GameInfo
file and navigate back to Unity.
Select the GameInfo GameObject in Hierarchy and assign it to the object fields by left-clicking and dragging the corresponding GameObject onto the fields:
Previously, we changed the Start
function to Awake
. If you missed this step, go ahead and change it now and enter the following code in the function:
// When object starts void Awake( ) { if ( FadeObject != null ) { FadeObject.transform.position = new Vector3( 0.5f, 0.5f, 0.0f ); FadeTexture = FadeObject.GetComponent<GUITexture>( ); FadeTexture.pixelInset = new Rect (0.0f, 0.0f, Screen.width, Screen.height ); RestartGame( ); } }
Let's start by making sure that we have a reference to FadeObject
. When we do, we then set the location of FadeObject
back to default because we have it offset in the editor so that it doesn't get in the way of us working in the scene.
We then assign the global GUITexture
variable named FadeTexture
to the component of the FadeObject
. This way, we can keep it stored in the class without having to always get it in other functions.
When we have the reference to the GUITexture
variable as FadeTexture
, we can change its pixelInset
to a new Rect
struct that starts at 0X
and 0Y
in the screen space (which is in the top-left corner). We can also set the size of the pixelInset
rect to that of the screen with Screen.width
and Screen.height
for its X
and Y
values for size.
This makes the black 2D box fit the size of the screen.
Lastly, we will call a function called RestartGame
. We will write this function soon; it will call all the things we need to make sure that the game resets, which includes the screen fading to black and back to transparent and resetting the Character
and LevelPieces
.
The Update
function for our GameInfo
class will handle the FadeTexture
for fading in and out of black, when to respawn the Character
, and reset the LevelPieces
.
Write the following code in the Update
function:
// Updates every frame void Update( ) { if ( bRestartingGame ) { if ( bStartFading ) { FadeToBlack( ); if ( FadeTexture.color.a >= 0.95f ) { bStartFading = false; } } else { FadeToNormal( ); if ( FadeTexture.color.a <= 0.5f ) { if (bLevelAndCharacterRestart) { // Restart level and characters before fade is done. RestartLevelAndCharacter(); } if (FadeTexture.color.a <= 0.05f) { bRestartingGame = false; FadeTexture.enabled = false; } } } } }
As the Update
function is called in every frame, we need to manage it in a way that it doesn't do any extra work when we don't want it to. This is why we have a few bool values.
To start with, we need to make sure that GameInfo
has set bRestartingGame
to true. This value can only be changed when the RestartButton is shown and only when the player has clicked on it.
Next, we need to check whether bStartFading
is set to true. When the RestartButton
is pressed, we will set this along with bRestartingGame
to true. The first thing we need to do when bStartFading
is set to true
is call the FadeToBlack
function. This function will be written to lerp between the current alpha value of FadeTexture
to a different value over the course of time. The thing with Lerp
is that you have to call it every time you want to change it, which is why we will use the Update
function. If we only call it once, it will only change the alpha value of FadeTexture
once.
After each frame that FadeToBlack
is called, we can change to see whether the alpha channel of FadeTexture
is greater than or equal to 0.95f
. At this value, the alpha channel has gone opaque (or black) enough for us to start fading back out. We can start fading back to transparent (or clear) by setting bStartFading
to false.
Once bStartFading
is set to false, we can start by calling FadeToNormal
, which will also use the Lerp
function, but in the opposite direction of FadeToBlack
, making the alpha channel of FadeTexture
become transparent.
Now, we can check whether the alpha channel is less than or equal to 0.5f
. We need to do this because at this time, the user can now begin to see the level, and we don't want the pause effect of the game to show through the fade. Once the alpha layer has reached this 0.5f
, we then check to make sure that bLevelAndCharacterRestart
is set to true
.
If bLevelAndCharacterRestart
is set to true, we can call the RestartLevelAndCharacter
function. This function will do as it's named; it will restart the Level and Character back to default. It also sets the bLevelAndCharacterRestart
bool to false, so this function can only be called once per restart.
We then continue to check the alpha channel of FadeTexture
until it is less than or equal to 0.05f
. When the alpha channel reaches this point, we know that the texture is transparent enough to set the bRestartingGame
bool to false and to disable FadeTexture
with FadeTexture.bEnabled
set to false.
To break this function down in a more simple way, when we restart the game, we can set bRestartingGame
to true. While this is true, we can fade the screen to black and then back to transparent. During the middle point of the transparency, we can reset the character and level so that the gameplay continues on seamlessly. When the transparency is finished, we can stop the reset by setting bRestartingGame
to false and disabling the FadeTexture
.
The
RestartGame
function is designed to start the sequence of code we just wrote for the Update
function and make sure that it is called when it is supposed to be.
Write the following function under our previously written PauseGame
function:
// Restarts the game public void RestartGame( ) { if (Time.timeScale == 1) { FadeTexture.enabled = true; bLevelAndCharacterRestart = true; bRestartingGame = true; bStartFading = true; HideRestartButton(true); } }
As we do not want to restart the game if the game is paused, we can start by making sure Time.timeScale
is set to one, which means that the game is running at its normal speed. If we were to allow the game to restart with a time scale that is less than one, we might experience an odd behavior, such as character and level restarting.
If timeScale
is set to one, we can enable the FadeTexture
because we will need it to begin fading in and then fading out the screen from black to transparent.
We then set a series of bool values that allows the Update
function to run its code in the correct way we want it. bLevelAndCharacterRestart
allows the level and character to restart. bRestartingGame
allows the fading to start happening in the correct order. Finally, bStartFading
allows the screen to start turning black.
Lastly, we will call HideRestartButton
, which will hide the RestartButton
from the scene because the player has already clicked on it and doesn't need to see it anymore.
This function is here so that the restart event can begin with the correct values.
The next step is to write the code that will actually reset the level and character back to default so that the game can continue. If you remember, this gets called from the Update
function if bLevelAndCharacterReset
is set to true and the alpha value for FadeTexture
is less than or equal to 0.5f
.
Under the RestartGame
function, write the following function:
// Restart level and character private void RestartLevelAndCharacter( ) { bLevelAndCharacterRestart = false; if ( GameCharacter != null ) { GameCharacter.ReviveCharacter( ); } if ( LevelManager != null ) { LevelManager.ResetLevelPieces( ); } }
This function is sort of a middle man to the restart process for the character and level. The one thing this function manages for the GameInfo
class is the first line of the function, which sets bLevelAndCharacterRestart
to false. When we set this to false, we can tell the Update
function that it can't call it again until we set it to true in the RestartGame
function, which is called from the RestartButton
.
We then check to make sure that the reference for GameCharacter
exists by verifying that it is not null. If we have the reference, we can call ReviveCharacter
from the Character
class, which we wrote in Chapter 3, Player Character, Obstacles, and Pickups.
Finally, we check to make sure that the LevelManager
reference exists by again verifying that it is not null. If it isn't, we can call the ResetLevelPieces
function. We will write this shortly. It will be a function that puts all the LevelPieces
back to their initial locations and resets the StartingLevelPiece
back to its initial location.
We now need to write the two functions responsible for fading our FadeTexture
from opaque to transparent and vice versa.
Under the RestartLevelAndCharacter
function, write these two functions:
// Fade screen to black private void FadeToBlack( ) { if (FadeTexture != null) { FadeTexture.color = Color.Lerp( FadeTexture.color, Color.black, 2.0f * Time.deltaTime ); } } // Fade screen back to clear private void FadeToNormal( ) { if (FadeTexture != null) { FadeTexture.color = Color.Lerp( FadeTexture.color, Color.clear, 2.0f * Time.deltaTime ); } }
These two functions do the same thing, but in the reverse order of each other. We first check to make sure that the FadeTexture
reference exists by verifying that it is not null. If we have a valid reference, we can say that the color value of FadeTexture
is set to the return value from Color.Lerp
.
Inside the FadeToBlack
function, we use the Lerp
function to change the FadeTexture.color
value from its current color value to the Color.black
value. This is done by taking the current value of FadeTexture.color
and adding to it every frame by the return value of Lerp
, which uses product of 2.0f
and Time.deltaTime
to get a percentage between the current FadeTexture.color
value and Color.black
value.
For the FadeToNormal
function, it does the same thing, but instead of fading to Color.black
, it starts to fade to Color.clear
, making the FadeTexture
value more transparent.
Remember that both of these functions are called in every frame when the bRestartingGame
is set to true. Each time either of them is called, they change the alpha value a small amount, resulting in a couple of seconds before the screen will fade completely black and then back to transparent, making a nice transition between pressing the RestartButton
and actually having the game restart.
The last function we need to write for GameInfo
is the HideRestartButton
function. The reason we need this is because we want the button to be shown only when we want it to be shown, and we do not want the player to reset the game unless they have died.
Under the FadeToNormal
function, write the following code:
// Shows or hides the restart butt public void HideRestartButton(bool bHide) { if (GameUI != null) { GameUI.transform.Find( "RestartButton" ).gameObject.SetActive( !bHide ); } }
Again, we need to make sure that the reference of the GameUI exists by making sure that it is not null. Once we have this reference, we can use the transform.Find
function to look for the GameObject named RestartButton, which is a child of GameUI. As Find
returns a transform, we have its gameObject
and then set this to the opposite of the function argument. We do this because if we were to take the value passed in and use it, it would make the function name look backwards.
For example, if we didn't use the opposite of what is passed and you passed true, it would set the button to active or shown. As the function name is HideRestartButton
, it makes more sense that if you pass true, it hides it, and if you pass in false, it shows it.
In the
Assets/Scripts
folder, double-click on the LevelPieceManager
file to open it. Under the isActivePiece
function, at the bottom of the class, write the following function:
// Resets all LevelPieces public void ResetLevelPieces() { for (int i = 0; i < LevelPieces.Length; i++) { LevelPieces[ i ].transform.position = LevelPieces[ i ].GetInitialLocation(); LevelPieces[ i ].ResetAllChildrenCoins(); } StartingLevelPiece.transform.position = StartingLevelPiece.GetInitialLocation( ); StartingLevelPiece.gameObject.SetActive( true ); Start( ); }
This function starts by looping through LevelPieces
. If you remember, LevelPieces
were assigned by us from the editor and the number of them is dependent on how many you assigned.
In the for
loop, we set the transform.position
of each LevelPiece
to the value of what is returned from the GetInitialLocation
function. This means that the level pieces assigned will move to where we had set them in the scene before any of them were used.
We then do the same thing for the StartingLevelPiece
. The only addition is to make sure that the StartingLevelPiece gameObject
is active by setting SetActive
to true.
Lastly, we will call the Start
function, which is designed to begin moving the StartingLevelPiece
and by getting a secondary piece from the world and setting its location to the EndLocation
of the StartingLevelPiece
.
From here, the normal code takes over, and the LevelPieces
are moved and set based on the code we had originally written to manage them. This function is just there to make sure that they all go back to default before starting over.
Now, save the LevelPieceManager
file.
In the
Assets/Scripts
folder, double-click on the Character
file to open it.
We need to add a few things to this class in order to make the reset functionality complete.
To begin with, add the following global variables at the top of the Character
class:
public GameInfo Game; [System.NonSerialized] public Vector3 RestartLocation;
The Game
reference allows you to tell GameInfo
that the RestartButton
needs to show when the character dies.
RestartLocation
will give us a location to restart the character when it dies.
In the Start
function, add the following code:
// Use this for initialization void Start () { RestartLocation = gameObject.transform.position; }
We now need to update a couple of functions so that we can use these new references. Go to the KillCharacter
function and use the following code:
characterRigidBody.AddForce(new Vector2(Random.Range(-1, 1), 1) * 512); isDead = true; isFadeOut = true;
Then, add the following code:
if (Game != null) { Game.HideRestartButton(false); }
This will show the RestartButton
on screen when the character is killed, allowing the player to restart the game.
We also need to update the ReviveCharacter
function so that DistanceCount
gets reset when the character does, and the character uses the RestartLocation
when it's revived.
First, change Vector3 resetLocation = new Vector3( 0.0f,0.0f,0.0f );
to Vector3 resetLocation = RestartLocation;
.
This will force the location of the character back to where it was assigned in Start
.
Next, we want to reset DistanceCount
when the character is revived.
Under the following code:
if (gameCharacterSprite != null) { gameCharacterSprite.color = resetColorAlpha; }
Add the following code:
DistanceCount = 0;
This will set DistanceCount
back to zero; so, when the game restarts, so does the value of distance and in turn, the text shown on the UI will also be updated to show zero. The reason for this is that we don't want the distance to continue through to multiple attempts.
Save the Character
file.
To complete the RestartButton
, we need to make sure that its click event is set to the RestartGame
function we wrote in GameInfo
.
Go back to Unity, select the RestartButton GameObject from the GameUI children in Hierarchy.
In Inspector, search for the Button
script component and click on the small plus symbol in the On Click () area.
Then, left-click and drag the GameInfo GameObject onto the new object field of the On Click () area for the RestartButton.
Click on the drop-down list next to the object field that is now holding the GameInfo GameObject. Then, navigate to GameInfo and select RestartGame:
If you play the game now, you should see the screen fade from black and have the Character animation play running. The LevelPieces
should now be moving and connecting together. The GameUI elements should also be updating the distance traveled and the coins collected.
Also, if you die, the RestartButton should show. When you click on the RestartButton, it should hide, and the screen should fade to black and then back to transparent. During this transition, the character and level is reset.
Saving data using the PlayerPrefs
class is not very difficult. Basically, the PlayerPrefs
class is designed to save the basic variable data so that it can be used between game sessions. In our case, we need to save two things. The first is the CoinCount
in Character
. We also need to check whether the user has bought the remove ads feature of the game.
For this chapter, we will start by making sure that the coins are saved between game sessions.
Go to the Assets/Scripts
folder and open the Character
file.
In the Start
function, add the following code under where we assigned RestartLocation
:
CoinCount = PlayerPrefs.GetInt( "Coins" ); AddCoins( 0 );
This will set CoinCount
to whatever has been saved. We can also use the AddCoins
function and pass zero so that the GameUI updates the UI to show how many coins we have.
Next, we need to see where to save the coins. As we don't have to save the PlayerPrefs
value every time a coin is picked up, we can use the point at which the player is killed to do so. This way, we aren't saving every coin and saving some overhead as well.
Find the KillCharacter
function and under the following code:
if (Game != null) { Game.HideRestartButton(false); }
Add the following code:
PlayerPrefs.SetInt("Coins", CoinCount); PlayerPrefs.Save();
Now, if you play the game, collect some coins, and stop playing, when you play again, it will show the coins you collected in the previous session as how many you have.
If you are on a Windows machine, the save data is saved in the registry. To get there, click on the Start button and enter regedit
. This will show a program called regedit.exe
. The save data is in HKEY_CURRENT_USER/Software/[YourCompanyName]/[YourGameName]
.
For example, mine is in HKEY_CURRENT_USER/Software/dotvawxgames/EndlessRushRunner
:
If you want to set your company and game name, navigate to Unity, click on Edit and then Project Settings, and select Player from the list. In Inspector, the top two settings are Company Name and Product Name. Set these, and the save file will use them as the directory in the registry.
If you are on a Macintosh computer, the save data is stored in the Library | Preferences folder in a file named unity.[company name].[product name].plist
.