Finally we are ready for the third and final part of the Breakout tutorial series.

As I said at the end of the last part, this time we will complete the tutorial by introducing Bricks, scoring and end game conditions, so let’s start with Bricks.

Add a New ▶ GameObject,call it “Brick” and, once again, add a New ▶ Graphics ▶ SpriteRenderer; set its properties as follows:

  • set Rect to [-20, -10, 40, 20]
  • set SharedMaterial to Default\Material\SolidWhite (from Project View)

Add a New ▶ Physics ▶ RigidBody, remove the default collision circle and replace it with a rectangle whose vertices coincide with those of the SpriteRenderer (so you will need [-20, -10], [20, -10], [20, 10] and[-20, 10]);
set AngularDamping, LinearDamping, Friction to 0.0 and Restitution to 1.0, and finally set its BodyType to Static.

Now, we will want our Bricks to have a different durability from one another, and in this case we will also need a way to determine how much durability a single Brick has left; so let’s head back to the IDE and create a new Brick class, deriving, as usual, from Component and fill in the code as follows:

You will notice something different from usual: this time, instead of having a public auto-property with get and set methods, I explicitly created the private backing field _hitPoints; this is due to the fact that we want the Brick to change its color (from red to black) depending on the remaining HitPoints.
In addition, the backing field was decorated with the [NonSerialized] attribute, due to the fact that we won’t be saving the Bricks inside our Scene directly, but rather we will implement also a generation mechanism to do the hard work for us.

Setting a field as [DontSerialize] is basically telling the engine that there is no need to save the information contained in the field during the serialization process, which happens when you save your GameObject or Resource. This can be useful when you are dealing with fields that contain volatile data (i.e. data that you don't mind losing every time, or that might be even dangerous to keep stored) and that you are sure will be re-initialized correctly when required.

Lastly, I added a bool Hit() method, which will be called every time the Ball hits a Brick, in order to decrease the Brick’s remaining HitPoints and, eventually, removing it from the Scene altogether; the method returns true if the Brick was destroyed, false otherwise (we will use this information for scoring).

Add the New ▶ Breakout ▶ Brick component to our Brick GameObject and then drag the Brick GameObject and drop it inside the Project View.
Congratulations! You just created a Prefab!

Prefab is short for "prefabricated"; a Prefab resource can be considered as a template that we can use to generate an arbitrary number of GameObjects, and that all these GameObjects will contain exactly the same Components and properties. Each object created this way will have the same name as the Prefab.

Once we have our Prefab, you can safely delete the Brick GameObject from the Scene.. we will make them at run time by ourselves. To do this, we will need a specialized Component, which we’ll call “GameManager”.

This GameManager will be in charge of the following:

  • generate the game field by instantiating Bricks and placing them at the right coordinates,
  • take care of calculating the score of the player
  • take care of resetting the Ball in case it goes out of bounds OR a new game is started (this will mean that our Ball won’t do that by itself anymore)
  • manage the lives left and, once they are gone, stop the game

For the first point, I will use a hardcoded approach, based on the fact that we know how much space there is around the game area and where we can put our Bricks. A good exercise would be to implement a new method which would generate the levels by selecting a template (could be an array where each index is a subsequent level) written using some kind of convention that defines the place and durability of each brick.
The second point will be implemented in conjuction with the Ball-Brick collision logic: every time a Brick is hit, we will add 1 point to the player’s current score; 5 points if the brick is destroyed.
The third and fourth points will be in fact managed by the same method, as you will see later, since a Ball going out of bounds is directly related to the amount of lives left for the player.

Again, some things you will (or should) notice in the code:

  • Once again I use [NonSerialized] private fields to hold the score and lives; as I explained before, there is no need to store the score inside the game’s Resources and, even if you were to implement a Save/Load mechanism to keep on playing a game from an arbitrary point, it would be better to save the current information inside the save file itself without cluttering the scene.
  • You might ask what is BrickPrefab and why do we need it: a ContentRef<T> is basically a link to a dynamically-loaded Rresource - whatever its type (defined, you guessed right, by T) and, in our case, it is needed in order to tell our component which one is the correct Prefab that should be instantiated as a “Brick”. Just drag the Brick Prefab from the Project View and drop it in the corresponding cell in the editor.
  • to remove the connection between a GameObject and the Prefab it was based on we use BreakPrefabLink(); I guess there is no harm in keeping it, as long as you don’t alter the Prefab using GameObject.PrefabLink methods. I just prefer to stay safe :) and also it keeps the game environment clean in my mind.
  • I always use this.GameObj.ParentScene to determine which Scene a new GameObject should be added to because, unless Scene.Current, it lets you work with Components in Scenes that are not the currently active one; for example, you might need to generate a level in one Scene, while currently you are showing another Scene as a loading screen. Again, it’s just what I feel is best.. I might be wrong :)

Right, so now that we have our level generation code sorted out, we need a way to trigger it. To this end, we will modify our InputController so that, when Enter is pressed and there are no Bricks in the Scene, we will reset the Ball to its starting position, and lay out a new level to play in.

As you can see, we added a property to reference our GameManager object, and the new condition to trigger the level generation logic; I have added a Scene.FindGameObject() call in order to verify that there are no GameObjects called “Brick” in the Scene before effectively preparing a new level.

Finally, we can now complete our Ball class by adding the Ball-Brick collision code; in order to use the code we wrote for the GameManager, we will have to add a reference to it inside our Ball class as well.

All right, that was long!
Compile your project and, if everything went fine, you should be able to add your New ▶ Breakout ▶ GameController to the Scene and link it everywhere it is needed (the Ball, and the InputController).
Go on, try to Play around for a while :)

I didn’t expect for this part to be so long.. I will stop here and finish with the scoring and general UI concepts next time.

See ya!