Jump to content
  • Advertisement
  • entries
    44
  • comments
    19
  • views
    2500

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

Josh Chang

744 views

Side Note: I've been feeling sick recently and progress have been slow, but I'm feeling better and ready to get back to it!

Welcome back to day 33!

Yesterday, we looked at 3 ways we can save and load data in Unity: with PlayerPrefs, Data Serialization, and saving our data to a server.

Today we’re going to use what we learned the previous day to save our score in our simple FPS.

Here’s the goal for today:

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

So, let’s get started!

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.

While we can technically use our PlayerPrefs anywhere we want between our scripts, it’s better for us to create a manager where we will centralize everything all our Saving/Loading work so that when we need to make changes, we don’t have to comb through all our Script to fix things.

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

In our SaveManager, we want to be able to save and load our high score. Here’s what we’ll have:

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.

We never want to manually type our key in as that might lead to us mistyping and many hours spent debugging over a single spelling mistake.

Walking Through the Code

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

The beauty of this system is that if one day we decide that we don’t want to use PlayerPrefs and use DataSerialization instead, we can just change SaveManager, but everything else that’s using it can stay the same.

Here’s the code flow:

  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.

To do that, we need to use the Victory and GameOver panels that we made in the past. Luckily for us, in our GameManager, we already have some code that uses them.

Now, for our GameManager to access our time (and save it with our SaveManager), we need to expose the score for other scripts to access them.

Here are our changes to ScoreManager:

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

That’s it! Now let’s combine everything we’ve worked on to create our high score system.

Step 1.3: Use Everything in Our GameManager

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

Now we’re going to write some code that allows us to save our when the player wins.

Here’s what we’ll have:

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.

Now with our SaveManager in GameManager, we can save and load scores.

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

With our changes to the GameManager, we now have a working high score system.

Now the problem? We have no way of seeing if any of this works!

Worry not, that’s going to be the next step of our work!

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.

To do this, we’re going to make a couple of changes to our script, we’re going to:

  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.

We’re going to make some changes to change the text in our Panel to also say what our high score is.

Let’s get to it! Here are the changes that were made:

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.

One important thing I want to mention. Do you notice the “\n”?

\n is an escape character for new line. Which means that in our UI, we’ll see something like:

Game Over

High Score: 1:21:12

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.

We must make some pretty big changes to our Game Panels that we use. Before we just grabbed the animator component, now, we need that and the GameOverUIManager.

Besides that, we just need to call our GameOverUIManager script and set the text with our high score.

Here’s what we’ve done:

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.

Before these were Animators that we used to show our panel, now we have the game objects themselves because we need to access more than just the animator.

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.

From this information, we play our animator and get the GameOverUIManager and call SetHighScoreText() to change the text.

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.

Here’s what to do:

  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.

With that done, we should have something like this:

adding-game-objects-back-to-GameManager-

Step 2.4: Fixing our Text UI Display

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

After winning (or losing) in our game, we’ll get our BRAND-NEW panel:

win-but-our-score-doesnt-get-shown-1-102

New… right?

Wrong! It looks the same as what we had!

What happened?

It turns out, the text is still there, however, we didn’t have enough space in our Text UI to show the remaining text!

This problem can be solved easily. We just need to increase the size of our Text UI in our GameOver and Victory Panel in our hierarchy.

The first thing we need to do is reveal our Panels that we hid.

In our Panels, find the CanvasGroup component, we previously set our alpha to be 0 to hide our panel. Let’s change that back to 1 so we can see our panel again.

setting-alpha-to-be-1-so-we-can-see-the-

Just don’t forget to change it back to 0.

In the same Panel, select its child game object, Text.

We want to try playing around with the Width and Height field in our Rect Transform component.

I ended up with:

Width: 300

Height: 80

I also added some new Text for a demo of what to expect. Here’s what we have now:

game-over-panel-increasing-text-size-1-1

Make sure that you make this change to both our GameOver Panel and our Victory Panel in the hierarchy.

Now if we were to play the game, here’s what we’ll have when we win:

win-panel-1024x454.png

And when we lose:

game-over-panel-1024x467.png

Don’t ask how I won in 2 seconds. I modified our scripts a bit, okay?

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!

Not only that, now that we have this high score system, I’m going to officially call our simple FPS finished!

Tomorrow, I’m finally going to start looking more into how to do VR development!

Until then, I’ll see you all on day 34!

Day 32 | 100 Days of VR | Day 34

Side topic: Choosing a phone and platform to develop VR in

Home



0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By rogerdv
      Im looking for a pixel artist to improve the current graphics of a mobile game. Here is a video of current beta. Knowledge of unity3d is a plus.
       
       
       
    • By ggenije
      On picture 1 is actually what I get, but how to make it to look like pic 2?

    • By Mercutio604x
      Hi,
      I am using unitys mecanim system
      I have a layer that controls movement ie idle -> run
      it works fine when I press W I run forward with the animation via transform.position += anim.deltaPosition; // anim is on a child object that has the animator
      I have a second layer for attack set to override 100%, on this layer I have just the head moving and is masked just for the neck up. it plays when I press the mouse button.
       
      Now my problem is when I press W and mouse button, it plays both animations BUT it stops moving forward.
      Theres no location key frames on the attack or any key frames on the root node for that matter.
       
      Please tell me if I am being unclear.
      Thanks in advance,
       
      Also I lost my other acc because I cant remember my pass or the email i used, is there a way I can get it back? like I say the account name and it tells me which email i used?
    • By tspartant
      Hey everyone! My name is Ryan. 
       
      Visualistic Studios is looking for experienced developers of all talents to join a game development team focused on completing contract work for compensation. 
       
      Work Description
      Typically you will either be assisting the team or working on your own contract.
      We usually bid $16-$25/h, however contracts can go above and below that so all pay grades are welcome, just be realistic. 
       
      Short Term Contracts
      Long Term Contracts
       
      We have the highest priority for these skills right now
       
      Programming - Unity, Unreal Blueprints
      Environment Artist
      Character Artist
      Character Animation
      UI Artist
      3D Asset Optimization
       
      VR/Mobile experience is a plus. 
       
      The Process 
      All communication is done through discord. All tasks and design documents will be laid out in "HackNPlan" for organization. 
      Initially, you'll get in contact with me and answer a few questions so I can get a scope of your experience. Afterwards, our outreach team will start looking for jobs that fit your description. Nothing is guaranteed, but if we know you're interested we can start looking 
       
      Our Experience
      For the past 3 years I've been working in game development contracting, and the past year I've been working full time from home. Since then, I've received more and more contracts and I'm now at the point that I have too many for myself to handle. This sparked the idea of creating a game development team for contract work! I've also been running my own hobby company for 5 years, and have a lot of experience in team management. 
       
      Get in contact!
      Please fill out this form so we can get all of the information out of the way, then we'll get in contact with you!
      Thank you everyone for reading, hope to hear from you soon!
    • By addictCoderCS
      Hi there,
      I'm working on an web RPG. This is not and action RPG. 
      The problem is I work full-time as a software developer and while I may get some code for the game done at work, I'm still a little short on time. So I'm looking for a second programmer to help me out. Please no beginners. I prefer working with someone who has built a full game (client, game server, web services, db)
      Requirements:
      Proficient in C# Proficient in .Net Core 2.X Experience with ASP.Net Core MVC Experience with ASP.Net Core Web API Experience with Unity 2018 Proficient in SQL and SQLite Proficient in EntityFramework Experience in AWS (RDS and EC2) Experience with IIS I'll handle the cost of any third-party services, domain names, etc. I'm just looking for a little help to get this game built in a reasonable amount of time. It will also be nice to bounce some ideas off each other.
       
      If you are interested, please send me an email: addictcodercs@gmail.com
       
       
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!