Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / game / Unity

Day 97 of 100 Days of VR: Endless Flyer – Saving our Coin and Score in PlayerPrefs

0.00/5 (No votes)
6 Feb 2019CPOL8 min read 2K  
How to save our coin and score in PlayerPrefs

Introduction

Here, we are at what will be the last series before we hit the big 100.

Reflecting at these past 10+ posts, I’ve realized that we transitioned from VR back to more game development.

My only regret is not transitioning to an easier content medium (like videos) and instead, I had to type everything out. As a result, I feel like my progress just isn’t nearly as fast as it could have been. However, it’s too late now and with only 4 more articles to go, I’m not going to turn back!

Transitioning back to today’s goal…

For the past 12+ days or so, we’ve been working on adding power-ups into the game. In the next 4 days, we’re going to create a simple item store where we can use the coins that we collect in the game to improve our existing upgrades.

To do that however, we need to be able to save the coins that we have gathered in our game.

Our goal today is to:

  1. Save our coins and score when the game is over
  2. Show the high score on our game over screen

We got quite a bit of things to do, so let’s get to it.

Step 1: Saving Our Coin and Score

Step 1.1: Create a Manager to Save and Load Our Coins and Score

I’m not going to go over how to save and load data, review Day 32: Learning about Saving and Loading in Unity for a quick recap on the different ways of storing data.

For our game, we’re not going to use anything fancy, we’re just going to use PlayerPrefs to save our information.

To do that, we’re going to create a simple Data helper script that will help us save and load our data.

In the project folder, let’s create a new script called DataManager (it’s not necessary for us to attach this script to any game objects).

DataManager will be used to save and load the coin and score that we collect. Here’s what it looks like:

C#
using UnityEngine;

public class DataManager 
{
    private static string _coinKey = "Coin";
    private static string _scoreKey = "Score";

    public static int LoadCoin()
    {
        return PlayerPrefs.GetInt(_coinKey, 0);
    }

    private static void SaveCoin(int coin)
    {
        PlayerPrefs.SetInt(_coinKey, coin);
    }

    public static int LoadScore()
    {
        return PlayerPrefs.GetInt(_scoreKey, 0);
    }

    private static void SaveScore(int score)
    {
        PlayerPrefs.SetInt(_scoreKey, score);
    }

    public static void AddCoin(int coin)
    {
        int totalCoin = LoadCoin() + coin;
        SaveCoin(totalCoin);
    }

    public static void AddNewScore(int score)
    {
        if (LoadScore() < score)
        {
            SaveScore(score);
        }
    }
}

Walking Through the Code

One important thing to note is that these functions are static. What this means is that we can call them anywhere.

Another important note is that to use PlayerPrefs, we have to make sure that whatever script that calls our helper class must extend MonoBehavior and be called after Awake() or Start().

Here’s what our helper class does.

  1. LoadCoin() returns the coin that we saved in our game under the string Coin (from _coinKey). If we didn’t save anything there, we will return 0.
  2. SaveCoin() saves the given coin value to our PlayerPrefs under the string Coin, because we have certain rules we want to enforce, the class is private and can only be accessed with AddCoin()
  3. AddCoin() is our public interface that we use to add our new coins we collected. In the function, we get our existing coin and add it to the coin we collected and save the results with SaveCoin()
  4. LoadScore() returns the coin that we saved in our game under the string Score (from _scoreKey). If we didn’t save anything there, we will return 0.
  5. SaveScore() saves the given score value to our PlayerPrefs under the string Score, similar to SaveCoin(), we don’t want anything to be able to call it, so it’s only accessed with AddNewScore().
  6. AddNewScore() is the public interface that we use to keep track of our high score. In the function, we check to see if our score is higher than our high score. If it is, we save it.

Step 1.2: Using Our DataManager to Save Our Score

With this script, we can now save and load data whenever we want to. Luckily for us, we have already written a game over code that we can leverage to save our results.

Specifically, this all gets called in our PlaneCollider script when we collide against a wall.

From there, we call our PlayerManager, GameUIManager, and CameraManager. The appropriate place to add our GameOver functionality would be in our GameManager as it governs everything in the game.

Here’s what it looks like:

C#
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance;

    private int _coin = 0;

    void Start ()
	{
	    if (Instance != null)
	    {
	        // If Instance already exists, we should get rid of this game object
	        // and use the original game object that set Instance   
	        Destroy(gameObject);
	        return;
	    }

	    // If Instance doesn't exist, we initialize the Player Manager
	    Init();
	}

    private void Init() {
        Instance = this;
        _coin = 0;
    }

    // Called from outside function for when the player collects a coin.
    public void CollectCoin()
    {
        int scoreIncrease = 1;
        if (PlayerManager.Instance.ContainsPowerUp(PlayerManager.PowerUpType.Score))
        {
            scoreIncrease *= 2;
        }
        _coin += scoreIncrease;
        GameUIManager.Instance.SetCoinText(_coin);
    }

    // Return the number of coins that we have collected.
    public int GetCoin() 
    {
        return _coin;
    }

    public void GameOver()
    {
        DataManager.AddCoin(_coin);
        DataManager.AddNewScore(ScoreManager.Instance.GetScore());
    }
}

Walking Through the Code

We also need to implement GetScore() for ScoreManager so we can access our score.

Speaking of ScoreManager, we’re not going to refactor the code to fix this, but in retrospect, our GameManager should have also been keeping track of our score alongside our coin. They shouldn’t be 2 separate managers.

Moving on:

We create a public function GameOver() that PlaneCollider can later call that will save our score for when the game is over. In this function, we add the coins we collected and add our new score that we have achieved.

Step 1.3: Updating Our ScoreManager

C#
using UnityEngine;

public class ScoreManager : MonoBehaviour {

    public static ScoreManager Instance;

    private float _score = 0;

    void Start()
    {
        if (Instance != null)
        {
            // If Instance already exists, we should get rid of this game object
            // and use the original game object that set Instance   
            Destroy(gameObject);
            return;
        }

        // If Instance doesn't exist, we initialize the Player Manager
        Init();
    }

    private void Init()
    {
        Instance = this;
        _score = 0;
    }

    void Update()
    {
        // increase our score and then update our ScoreText UI.
        float increaseTime = Time.deltaTime * 10;
        if (PlayerManager.Instance.ContainsPowerUp(PlayerManager.PowerUpType.Score))
        {
            increaseTime *= 2;
        }
        _score += increaseTime;
        GameUIManager.Instance.SetScoreText((int)_score);
    }

    public int GetScore()
    {
        return (int) _score;
    }
}

Walking Through the Code

To make our GameManager script work, we need to implement GetScore(). All we do in our function is return our score value.

With this, our code now works correctly.

Step 1.4: Calling GameOver from PlaneCollider

Now that we have everything ready to make the game work, it’s time to use it in PlaneCollider. Here’s our small change:

C#
using UnityEngine;

public class PlaneCollider : MonoBehaviour 
{
	public GameObject PlaneObject;
    public GameObject Explosion;
    public AudioClip MagnetSFX;
    public GameObject MagnetParticleEffect;
    public AudioClip MultiplierSFX;
    public GameObject MultiplierParticleEffect;
    public AudioClip InvincibleSFX;

    private SoundManager _soundManager;
    private GameObject _currentParticleEffect;

    …  

    /// Destroy the player and set the current state to the dead state.
    private void EnemyCollision()
    {
        if (!PlayerManager.Instance.ContainsPowerUp(PlayerManager.PowerUpType.Invincible))
        {
            Instantiate(Explosion, PlaneObject.transform.position, Quaternion.identity);
            Destroy(PlaneObject);
            GameManager.Instance.GameOver();
            PlayerManager.Instance.GameOver();
            GameUIManager.Instance.GameOver(gameObject);
            CameraManager.Instance.GameOver();
        }
    }
}

Walking Through the Code

In EnemyCollision(), we just call our newly implemented GameOver() which will save our coin and score.

Step 2: Showing Our Coin and Score in the Game Over Panel

Step 2.1: Change the UI

The first thing we need to do to show our next text is to modify our Game Over Canvas.

We want our canvas to look like this:

Image 1

To make this, we should:

  1. Drag our GameOverCanvas prefab from our Prefab folder into the game, we need to make some modifications
  2. Duplicate the Restart Button and call it Shop Button
  3. Duplicate the Game Over Text and call it High Score Text, change the font size to be 16
  4. Duplicate High Score Text 2 more times and rename them Score Text and Coin Text

Now that we have our new UI pieces, let’s change their values:

  1. Game Over Text change Pos Y to be 3
  2. High Score Text change Pos Y to be 10
  3. Score Text change Pos Y to be 0
  4. Coin Text change Pos Y to be -10
  5. Shop Button change Pos Y to be -17.6
  6. Restart Button change Pos Y to be -35

Here’s what it looks like now:

Image 2

Step 2.2: Making Changes to Show the New UI Value

Now that we have our new UI, we need to write some code to add the correct text value to our UI. We can do this inside the Game Over Canvas Controller script which is attached to our Game Over Canvas.

Here’s what our change looks like:

C#
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class GameOverCanvasController : MonoBehaviour {
    public Text HighScoreText;
    public Text ScoreText;
    public Text CoinText;

    /// <summary>
    /// Event callback for when the player clicks on the Restart Button in the menu
    /// </summary>

    public void ClickRestartButton()
    {
        print("clicked the button");
        SceneManager.LoadScene(0);
    }

    public void ClickShopButton()
    {
        SceneManager.LoadScene(1);
    }

    public void GameOver()
    {
        HighScoreText.text = "High Score: " + DataManager.LoadScore();
        ScoreText.text = "Score: " + ScoreManager.Instance.GetScore();
        CoinText.text = "Coin: " + GameManager.Instance.GetCoin();
    }
}

Looking at the Fields

  • public Text HighScoreText – This is the text that we will use to display the high score
  • public Text ScoreText – This is the text that we will use to display the score we got in the current round
  • public Text CoinText – This is the text that we will use to display the coin we collected in the current round.

Walking Through the Code

  1. GameOver() this will be a public function that will be called so that we will know to populate our UI with the high score, score, and coin we received in the game. We get the values from the appropriate managers.
  2. ClickShopButton(), we added a new shop button that we will use later to take us to a new item shop scene. For now, that scene doesn’t exist, but we’ll add it in later.

Setting Up the Script

Now with these changes, we just need to drag and drop the scripts to the appropriate sections:

  1. Drag each of the Text game objects inside the Game Over Canvas into the appropriate slot in the GameOverCanvasController script component.
  2. In ShopButton, in the Event Trigger component, change the PointerClick to call ClickShopButton instead of ClickRestartButton
  3. Finally, go back to the Game Over Canvas object and hit Apply to save our change to the prefab.

With these changes, we will now correctly show our coin and score, but nothing will happen yet when we click on the Shop button.

Step 2.3: Call GameOver from the GameUIManager

We’re almost there, there’s just one more thing to do, we need to call GameOver when we display our GameOverCanvas in the GameUIManager.

Here’s what the script looks like:

C#
using UnityEngine;
using UnityEngine.UI;

public class GameUIManager : MonoBehaviour
{
    public static GameUIManager Instance;
    public GameObject GameOverCanvas;

    void Start()
    {
        if (Instance != null)
        {
            // If Instance already exists, we should get rid of this game object
            // and use the original game object that set Instance   
            Destroy(gameObject);
            return;
        }

        // If Instance doesn't exist, we initialize the Player Manager
        Init();
    }

    private void Init()
    {
        Instance = this;
    }

    /// <summary>
    /// Sets the GameOverUIManager to be in the game over state.
    /// </summary>

    public void GameOver(GameObject player)
    {
        GameObject gameOverCanvas = Instantiate
              (GameOverCanvas, player.transform.position, Quaternion.identity);
        GameOverCanvasController gameOverController = 
               gameOverCanvas.GetComponent<GameOverCanvasController>();
        if (gameOverController != null)
        {
            gameOverController.GameOver();
        }
    }
}

I think the code is simple enough by itself! One thing that I messed up is

End of Day 97

With these changes now, we should have a working UI that shows our score. Here’s what it might look like:

Image 3

Nice so to recap, today we:

  1. created a helper class to Save and Load our score and coin data
  2. updated our Game Over Canvas UI to show that includes our Score and Coins earned

With these changes in, we can transition to the next part of our item shop series and that is to create our new item store scene and create the basic UI for us to buy upgrades for our power-up and display our coins.

Stay tuned for the next post!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)