Creating the game user interface

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.

The GameInfo class

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:

  1. In the Assets/Scripts folder, right-click and select Create and then click on C# Script. Name this GameInfo.
  2. Double-click on the GameInfo script and select the Start function as Awake. Save this file and go back to Unity.
  3. Right-click on Hierarchy and select Create Empty. Name this new GameObject GameInfo.
  4. With this selected, search for Inspector and click on Add Component. Then, search for GameInfo and select GameInfo.

Importing UI images

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:

  1. Let's go back to Unity, navigate to the Assets folder, right-click and select Create and then select Folder. Name this folder UI.
  2. Right-click on the 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.
  3. Select the UI art files. Then, in Inspector, change the image settings as follows:
    1. Set FilterMode to Trilinear.
    2. Set MaxSize to 256.
    3. Set Format to Truecolor.
    4. Then, click on Apply.

Tip

You can select more than one file in the Project folder by left-clicking on one and holding down Shift to select more than one range. For example, select the top file in the folder and press Shift + left-click on the bottom file. This will select all of these files between the top and bottom.

Creating the UI Canvas

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.

The coin background

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:

  1. Set Pos X to -87.4.
  2. Set Pos Y to -103.2.
  3. Set Pos Z to 0.
  4. Set Width to 164.
  5. Set Height to 64.

Tip

If I do not list a change for any setting, leave it as default. This will be true for all the settings for the UI.

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:

The coin background

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.

Tip

Anchor is the position on the UI screen that will hold this element of the UI. The element that is anchored to this anchor will follow it, irrespective of the screen size.

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:

  1. Set Pos X to 19.6.
  2. Set Pos Y to 1.3.
  3. Set Pos Z to 0.
  4. Set Width to 103.6.
  5. Set Height to 41.1.
  6. Set Text to 0.
  7. Set Font to Arial.
  8. Set Font Style to Bold.
  9. Set Font Size to 36.
  10. Set Alignment to Right.

Tip

The alignment is set by the small buttons next to it. Select the third button from the left-hand side to set it to Right.

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:

  1. Set Pos X to -73.5.
  2. Set Pos Y to 0.
  3. Set Pos Z to 0.
  4. Set Width to 100.
  5. Set Height to 100.
  6. Set Source Image to 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:

The coin background

Before we set up the code to adjust the value in the CoinValue text component, we will set up the DistanceBackground field.

The distance background

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:

  • Pos X: -87.39999
  • Pos Y: -37
  • Pos Z: 0
  • Width: 164
  • Height: 64
  • Source Image: ButtonBackground
  • Anchor: top right

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:

  • Pos X: 0
  • Pos Y: -0.1
  • Pos Z: 0
  • Width: 141
  • Height: 41.6
  • Anchor: top right
  • Text: 0
  • Font: Arial
  • Font Style: Bold
  • Font Size: 36
  • Alignment: Right

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.

The pause button

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:

  • Pos X: 34.5
  • Pos Y: -34
  • Pos Z: 0
  • Width: 54
  • Height: 54
  • Anchor: top left
  • Source Image: 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():

The pause button

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.

Updating code to show coins and distance

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.

Updating the coin class

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.

The restart button

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:

  • Pos X: 0
  • Pos Y: 93
  • Pos Z: 0
  • Width: 128
  • Height: 64
  • Anchor: center bottom
  • Source Image: RestartButton

Select the Text child GameObject of the RestartButton, search for its Inspector, and change its settings as follows:

  • Text: Restart
  • Font: Arial
  • Font Style: Bold
  • Font Size: 32

The fade object

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:

  • Position X: -5
  • Position Y: -5
  • Position Z: 0
  • Texture: BlackFade
  • Color: 0—red, 0—blue, 0—green, 255—alpha

We 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.

The restart functionality

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:

The restart functionality
The Awake function

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

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.

Tip

Lerp comes from Linear Interpolation. It is a way to get a value that is some percentage between two values. This is a great tool if you want to slowly move between these two values, or if you want to get a value that is between two values.

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

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 RestartLevelAndCharacter function

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.

FadeToBlack and FadeToNormal functions

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 HideRestartButton function

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 );
      }
    }

Tip

If you get an error telling you that GameUI is not set to an instance of an object, make sure to take the GameUI GameObject in the Hierarchy tab and left-click and drag it into the object field of the GameInfo GameObject, which is also in Hierarchy.

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.

Now, save the GameInfo class.

The ResetLevelPieces function in LevelPieceManager

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.

Updating Character To Reset

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.

Connecting RestartGame to the RestartButton click

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:

Connecting RestartGame to the RestartButton click

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.

Tip

There has been a lot to do with the GameInfo GameObject and getting the game loop completed. If you are lost or having issues with code, take a look at the project files that came with the book and use them as reference.

PlayerPrefs game save object

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:

PlayerPrefs game save object

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset