Day 33 of 100 Days of VR: Implementing the High Score System

  1. Implement our SaveManager to help us save score
  2. Update our UI to show our high score when the game is over

Step 1: Saving our Data

Of the methods we’ve talked about, I’m going to use the PlayerPrefs to help us save our data.

Step 1.1: Creating our SaveManager

The first step to saving our score is to create our ScoreManager script and attach it to our GameManager game object.

  1. Select our GameManager game object in our hierarchy.
  2. In the Inspector, click Add Component and create a new ScoreManager
using UnityEngine;public class SaveManager : MonoBehaviour
{
private string _highScoreKey = "highscore";
public void SaveHighScore(float score)
{
PlayerPrefs.SetFloat(_highScoreKey, score);
}
public float LoadHighScore()
{
if (PlayerPrefs.HasKey(_highScoreKey))
{
return PlayerPrefs.GetFloat(_highScoreKey);
}
return 99999999999;
}
}

Variables Used

For our SaveManager, we only create a string _highScoreKey that we use to store the text that we want to use for our score.

Walking Through the Code

Our SaveManager script is only used to help us access our PlayerPrefs in one centralized location.

  1. In SaveHighScore() we save the score that we’re given to our high score.
  2. In LoadHighScore() we return the high score that we saved. It’s important to note that if we don’t have a value in our Prefab, we would return 0, however, in our case, a lower score is better, instead we return a very high score.

Step 1.2: Modifying our ScoreManager to Expose Our Score

Previously, we had our ScoreManager change the text of the score in our page, however, if we want to be able to show our high score at the end of the game.

using System;
using UnityEngine;
using UnityEngine.UI;
public class ScoreManager : MonoBehaviour
{
public Text Score;
private string _time;
private bool _gameOver;
private float _score;
void Start ()
{
_time = "";
_gameOver = false;
_score = 9999999999;
}
void Update()
{
if (!_gameOver)
{
UpdateTime();
}
}
private void UpdateTime()
{
_score = Time.time;
_time = ScoreManager.GetScoreFormatting(Time.time);
Score.text = _time;
}
public void GameOver()
{
_gameOver = true;
}
public float GetScore()
{
return _score;
}
// we can call this function anywhere we want, we don't need to have an instance of this class
public static string GetScoreFormatting(float time)
{
int minutes = Mathf.FloorToInt(time / 60);
int seconds = Mathf.FloorToInt(time % 60);
float miliseconds = time * 100;
miliseconds = miliseconds % 100;
return string.Format("{0:0}:{1:00}:{2:00}", minutes, seconds, miliseconds);
}
}

New Variables Used

We create a new float _score that we’ll use to keep track of the time that passed for the player.

Walking Through the Code

Most of the code is the same. We just wrote some new functions, here’s what we did:

  1. In Start() we set _score to have a starting value
  2. In UpdateTime() we update _score to be the current time in the game. I also moved the code that gave us the time in a nice: minutes:seconds:milliseconds format to a static function called GetScoreFormatting() where we used to set our _time. Notice how I use GetScoreFormatting()? GetScoreFormatting() can be used just like this anywhere else in our game now, even if we don’t have an instance to ScoreManager.
  3. GetScoreFormatting() is just a copy and paste of what we originally had in UpdateTime().
  4. Finally, we create a public function GetScore() to get the score the player earned in GameManager when they win

Step 1.3: Use Everything in Our GameManager

Now that we have everything we want, let’s use it in our GameManager script!

using UnityEngine;public class GameManager : MonoBehaviour
{
public Animator GameOverAnimator;
public Animator VictoryAnimator;
private GameObject _player;
private SpawnManager _spawnManager;
private ScoreManager _scoreManager;
private SaveManager _saveManager;
void Start()
{
_player = GameObject.FindGameObjectWithTag("Player");
_spawnManager = GetComponentInChildren<SpawnManager>();
_scoreManager = GetComponent<ScoreManager>();
_saveManager = GetComponent<SaveManager>();
}
public void GameOver()
{
GameOverAnimator.SetBool("IsGameOver", true);
DisableGame();
_spawnManager.DisableAllEnemies();
}
public void Victory()
{
VictoryAnimator.SetBool("IsGameOver", true);
DisableGame();
if (_scoreManager.GetScore() < _saveManager.LoadHighScore())
{
_saveManager.SaveHighScore(_scoreManager.GetScore());
}
}
private void DisableGame()
{
_player.GetComponent<PlayerController>().enabled = false;
_player.GetComponentInChildren<MouseCameraContoller>().enabled = false;
PlayerShootingController shootingController = _player.GetComponentInChildren<PlayerShootingController>();
shootingController.GameOver();
shootingController.enabled = false;
Cursor.lockState = CursorLockMode.None;
_scoreManager.GameOver();
}
}

New Variables Used

The first thing we did was we got ourselves an instance of SaveManager: _saveManager.

Walking Through the Changes

The code that we’re adding uses our SaveManager to save our score.

  1. In Start() we instantiate our SaveManager which is also attached to the GameManager game object.
  2. When Victory() is called, we check their current score and then compare it with our high score from our SaveManager if our time is lower than the high score, than we set our score as the new high score

Step 2: Update the Game Over Panels to Show Our Score

Now that we have the code to change our high score, we’re going to work on displaying our high score in our UI.

  1. Change our GameOverUIManager to change the Text that we show.
  2. Change our GameManager to get our GameOverUIManager from our game over panels and then set the high score to show when the game is over.

Step 2.1: Making Changes to GameOverUIManager

If you recall, our GameOverUIManager was created to help us detect when the player clicks on the start over a button in our panel.

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class GameOverUIManager : MonoBehaviour
{
private Button _button;
private Text _text;
void Start () {
_button = GetComponentInChildren<Button>();
_button.onClick.AddListener(ClickPlayAgain);
_text = GetComponentInChildren<Text>();
}
public void ClickPlayAgain()
{
SceneManager.LoadScene("Main");
}
public void SetHighScoreText(string score, bool didWin)
{
print(_text.text);
if (didWin)
{
_text.text = "You Win! \n" +
"High Score: " + score;
}
else
{
_text.text = "Game Over! \n" +
"High Score: " + score;
}
print(_text.text);
}
}

New Variables Used

The only new variable that we used is a Text UI that we call _text. Specifically, this is the UI element that we use to tell the player that they won (or lost) the game.

Walking Through the Changes

The only changes we did was:

  1. Instantiate our Text UI in Start(). In the case of our panel, the Text was a child the panel that GameOverUIManager was attached to, so we have to look for the component in our child.
  2. Once we have an instance of our text, I created a new SetHighScoreText() that, depending on if we won or loss, change our text to show the high score.

Step 2.2: Calling our GameOverUIManager from GameManager

Next up, we want to be able to set the score from our GameOverUIManager from our GameManager.

using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
public GameObject GameOverPanel;
public GameObject VictoryPanel;
private GameObject _player;
private SpawnManager _spawnManager;
private ScoreManager _scoreManager;
private SaveManager _saveManager;
void Start()
{
_player = GameObject.FindGameObjectWithTag("Player");
_spawnManager = GetComponentInChildren<SpawnManager>();
_scoreManager = GetComponent<ScoreManager>();
_saveManager = GetComponent<SaveManager>();
}
public void GameOver()
{
DisableGame();
_spawnManager.DisableAllEnemies();
ShowPanel(GameOverPanel, false);
}
public void Victory()
{
DisableGame();
if (_scoreManager.GetScore() < _saveManager.LoadHighScore())
{
_saveManager.SaveHighScore(_scoreManager.GetScore());
}
ShowPanel(VictoryPanel, true);
}
private void ShowPanel(GameObject panel, bool didWin)
{
panel.GetComponent<Animator>().SetBool("IsGameOver", true);
panel.GetComponent<GameOverUIManager>().SetHighScoreText(ScoreManager.GetScoreFormatting(_saveManager.LoadHighScore()), didWin);
}
private void DisableGame()
{
_player.GetComponent<PlayerController>().enabled = false;
_player.GetComponentInChildren<MouseCameraContoller>().enabled = false;
PlayerShootingController shootingController = _player.GetComponentInChildren<PlayerShootingController>();
shootingController.GameOver();
shootingController.enabled = false;
Cursor.lockState = CursorLockMode.None;
_scoreManager.GameOver();
}
}

New Variables Used

The biggest change is with the GameOverPanel and VictoryPanel.

New Functions Created

We created a new function: ShowPanel(), which takes in the GamePanel that we’re changing the text to and whether or not we won the game.

Walking Through the Code

Here’s how our new code gets used:

  1. Whenever the game is over, either the player lost or won, we would call GameOver() and Victory().
  2. From there, we would disable our game and then call our new function ShowPanel()
  3. Depending on whether we won or not, we would pass in the correct Panel and state we’re into ShowPanel()
  4. Finally, in ShowPanel(), we would play the animation to show our Panel and call setHighScoreText() from our GameOverUIManager to change the text to display our high score.

Step 2.3: Attaching our Panels back into GameManager

Now with everything in place, we need to add our GameOverPanel and VictoryPanel, because when we changed them, we got rid of any reference to our previous models.

  1. Select our GameManager game object from the hierarchy.
  2. Look for the GameManager script component, drag our Panels (Victory and GameOver) from HUD in our game hierarchy, and put them in the appropriate slots.

Step 2.4: Fixing our Text UI Display

Now with all of this implemented, we can finally play our game!

Conclusion

With all of this, we’re done with day 33! Which means we’re officially 1/3 of the way through the 100 days of VR challenge!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Josh Chang

Josh Chang

Software Engineer by day, side hustler wannabee by night! https://leetdev.io