Hello and welcome to the 4th and last part of the tutorial on how to create a Candy Crush Saga clone.
In this last part, we will see how to add an in-game and a game over GUI, a nice background, and some audio effects for our game.
If this is the first time that you visit this tutorial series, you are maybe interested in the previous parts:
- Part 1: Setting up the project
- Part 2: Move cells
- Part 3: Identify matches
- Part 4: Add GUI and sounds
User interface
Inside the Prefab folder, I prepared a prefab called GameMenu. Drag it into the scene to instantiate it, and look at its composition:
In short, ScoreBackground and MovesBackground are the objects that will represent the score interface and the remaining moves respectively. GameOverMenu is the menu that appears when the game ends. This object has a component called GameOverManager that I have already created for you and is responsible for managing the display of scores and restarting the game when the user presses the appropriate button.
Let’s implement the logic of the scores within the GridManager:
public GameObject GameOverMenu; // 2 public TextMeshProUGUI MovesText; public TextMeshProUGUI ScoreText; public int StartingMoves = 50; // 2 private int _numMoves; // 3 public int NumMoves { get { return _numMoves; } set { _numMoves = value; MovesText.text = _numMoves.ToString(); } } private int _score; public int Score { get { return _score; } set { _score = value; ScoreText.text = _score.ToString(); } }
We added a total of three properties to the class:
- We have declared references for the interface objects that will display the status of the game.
- The number of initial moves. When the number of moves drops to zero, the game ends.
- The number of remaining moves and the score. For these properties we are going to define a custom setter, in which we will update the interface with the new value.
All that remains is to initialize these new variables:
void Awake() { Instance = this; Score = 0; NumMoves = StartingMoves; GameOverMenu.SetActive(false); }
and use them in our game. In real games, the score is calculated so that matches of multiple squares give more points, also chain matches provide even more points. For simplicity, the score is calculated based on the number of tiles eliminated. With each move, the number of available moves is then decreased by one. In the SwapTiles function:
... else { NumMoves--; do { FillHoles(); } while (CheckMatches()); if (NumMoves <= 0) { NumMoves = 0; GameOver(); } }
Let’s update the score by adding the number of deleted tiles, in the CheckMatches function:
Score + = matchedTiles.Count; return matchedTiles.Count> 0;
The new GameOver function will take care of activating the summary menu and storing the current score:
void GameOver() { PlayerPrefs.SetInt ("score", Score); GameOverMenu.SetActive (true); }
To store the score of the current game, we will use the PlayerPrefs object. This utility offered by Unity allows you to store some primitive variables (strings, integers and floats) by assigning them a label. This information is preserved between executions of Unity, thus providing a way to save data. The GameOverManager will read this score and compare it with the best score (always stored via PlayerPrefs) in order to display a warning if the user has completed the game with an High Score. To delete the saved record, use the “Reset high score” command in the Unity “Match three” menu.
The last thing to do before starting the game is to assign the references of the new properties from the editor. The final aspect of the GridManager component will be the following:
To assign a reference, simply drag the elements from the menu that we instanced in scene at the beginning of this chapter.
The last thing to do is to activate TextMeshPro, which is a plugin provided by Unity that allows you to use and configure interface texts in a more practical way than the default ones. Click on Edit -> Project settings and go to the TextMesh Pro section, and click on Import TMP Essentials.
Now we can press Play and see how the counter goes down with each moves and our score goes up. When the move counter drops to zero, the game over menu will appear with the words “New high score!” shown only if the user has beat the high score. Pressing the button the game starts from the beginning.
Adding a background
Lastly, let’s add a background. This step is very simple, we simply drag the prefab called background to the scene. I prepared this prefab for you so that it is always drawn under the grid and interfaces, also I darkened it slightly to make the grid stand out.
The end result will be this:
Instead, the GameOver screen:
Adding sounds
The last feature that we will add, are sounds. I have already created a SoundManager for you: drag prefab of the same name in scene. The newly instanced GameObject has a component only called SoundManager, which offers the ability to reproduce sounds. We will see how to use it in a very short time, but first let’s recap what we want to achieve. The SoundManager offers four different sounds that we will use for:
- Selection of boxes
- Elimination of boxes following a match
- When it’s impossible to swap cells or when two non-adjacent boxes are checked
- A game over jingle
Let’s add the following lines within GridManager, in SwapTiles:
... renderer1.sprite = renderer2.sprite; renderer2.sprite = temp; SoundManager.Instance.PlaySound(SoundType.TypeMove); } else { SoundManager.Instance.PlaySound(SoundType.TypePop); NumMoves--; do { ...
and in the GameOver function:
void GameOver() { Debug.Log("GAME OVER"); PlayerPrefs.SetInt("score", Score); GameOverMenu.SetActive(true); SoundManager.Instance.PlaySound(SoundType.TypeGameOver); }
Finally, in Tile.cs:
.... else { SoundManager.Instance.PlaySound(SoundType.TypeSelect); selected = this; Select(); } } else { SoundManager.Instance.PlaySound(SoundType.TypeSelect); ...
Also in this case, we use the Singleton pattern to get the reference to the SoundManager, which have PlaySound function that accepts a type. This type is an enum defined within the script, which includes four types, as we said earlier. The SoundManager, obtained the type as a parameter, will know which sound to play.
And now?
If you want to have the complete project, go to the project’s GitHub page and download the latest version! You can use Git or download a release from the download section.
Obviously what we have created is not a complete game, there are still many things to add to make it a finished product:
- An algorithm to identify situations where feasible are no longer present and the player is blocked. I leave the definition of this algorithm as an exercise otherwise this guide becomes really too long at least once matches are found then you can continue with the current grid)
- An animation system for tiles movement
- A home screen
- Level selection screen
- Optimization of the present code, which has been deliberately simplified to make this guide easier
What do you think about this tutorial? Please, let me know in the comments!
how to reshuffle the candy crush board if there is no more matches available.
thank you
Hey this project was fantastic, learned a lot from it.
I was wondering if i had to rotate all the sprite on it’s position, would be a good additional feature to the game.
Can you help me how it can be achieved.
( till now i have the idea that, the sprites must be rotated when they are instantiated, but not very clear how to do it)
Hey this project was fantastic, learned a lot from it.
I was wondering if i had to rotate all the sprite on it’s position, would be a good additional feature to the game.
Can you help me how it can be achieved.
( till now i have the idea that, the sprites must be rotated when they are instantiated, but not very clear how to do it)