How Create Brick Breaker Game In Unity.
VECTOR LABZ LLC
Boost your mobile game's quality without breaking the bank. Focus on Your Vision, We'll Handle the Development Grind.
Unity is a powerful and widely-used game development platform and engine known for its?versatility,?ease of use, and ability to create stunning interactive experiences across various platforms. With Unity, developers can build games, interactive applications, simulations, and more for a wide range of platforms, including?mobile devices,?consoles,? desktops, and even?virtual reality (VR)?and?augmented reality (AR)?devices. Its cross-platform capabilities make it a popular choice for game developers, indie creators, and large studios alike.
Designing a user interface (UI) holds immense significance in video game development, as it facilitates the presentation of crucial information to players, such as health, ammo, and scores. The effectiveness of a game largely depends on the accessibility of this information. Equally vital is the dynamic updating of this data as players engage with the game. For instance, if a player accumulates points, the UI must promptly reflect this progress by updating the on-screen score.
Breakout Ball game is a widely used game which was developed in the 1970s. In Breakout, a layer of bricks lines the top third of the screen and the goal is to destroy them all. A ball moves straight around the screen, bouncing off the top and two sides of the screen. When a brick is hit, the ball bounces back and the brick is destroyed.
The player loses a turn when the ball touches the bottom of the screen, to prevent this from happening, the player has a horizontally movable paddle to bounce the ball upward, keeping it in play. The player uses the platform to keep the ball running. The goal is to break the bricks without missing the ball with your platform. The project makes use of Java swing, OOPS concepts and much more.
To create a project in Unity you will need to have?Unity?and?Unity Hub?installed. You can?download?it from Unity's website. To complete the project, follow this tutorial from start to end.
1. Create Project:
First, we open the?Unity Hub?and?Create?a?2D Project.
For our project, we're in need of a variety of essential graphics to enhance the visual appearance and gameplay experience. These graphics include dynamic backgrounds that set the tone, walls that define the game space, paddles that players control, a lively ball that's at the heart of the action, and engaging bonus elements to add an extra layer of excitement.
In addition to graphics, sound plays a pivotal role in creating an immersive environment. We're seeking captivating background music that complements the gameplay and sets the mood. Additionally, we require a range of sound effects that punctuate the gameplay - from the satisfying impact of hits and the sense of loss to the rewarding sound of gaining points.
Fortunately, the resources we're looking for, including graphics and sounds, can be sourced from various platforms online without any cost. This ensures that we can access high-quality elements that contribute to the overall appearance of our project. By carefully selecting these elements, we're striving to create an engaging and dynamic gaming experience that captivates our players.
2. Setup Working Enivroment:
To ensure a well-organized and structured project, we'll start by creating a hierarchy of folders under the "Project > Assets" section. These folders will help us manage our assets efficiently and keep everything in its rightful place. We'll establish distinct categories such as "Music", "Physics", "Prefabs", "Scripts", "Sounds", and "Sprites" to accommodate the various elements needed for our game.
Under the "Prefabs" folder, we are taking a step further to create additional subfolders, namely "Blocks" and "Bonus". This subdivision streamlines our asset management even more, providing specific locations for prefab-related content. Similarly, within the "Sprites" folder, we are creating subfolders like "Backgrounds", "Balls", "Bonus", "Bricks", "Paddle", and "Walls". These subfolders align with the different graphic components of our game, allowing us to effortlessly locate and manage our sprite resources.
Once our organizational structure is set up, we shall proceed to populate these folders. Our carefully crafted artwork will find its place in the relevant sprite folders, ensuring that each graphical element is stored exactly where it belongs. Additionally, we shall import our chosen music and sounds, allocating them to the "Music" and "Sounds" folders respectively.
By following this meticulous approach to folder creation and asset organization, we're establishing a foundation for an efficient and well-structured game development process. This method ensures that every asset is easily accessible, making iterations, adjustments, and updates seamless as we progress with our project.
3. Add Sprites:
Let's proceed with scene creation by adding a new scene named "Level001".
Within this scene, we'll focus on refining the visual elements and tags to set the foundation for our game's mechanics.
Begin by selecting the "MainCamera" object and adjusting its background color to black, setting a fitting backdrop for our game environment.
Moving forward, we'll introduce new tags to categorize our game objects effectively. Tags such as "Background", "Ball", "Block", "Bonus", and "Wall" will allow for streamlined identification and manipulation of different elements. The following image just show tags and layers section and how to add tags but you need to change names of tags as required.
Now, we'll incorporate specific graphics for these components. Add an image for the background, renaming it to "Background" and assigning it the "Background" tag. Next, include an image for the walls, renaming it "Wall". Change its tag to "Wall", adjust its Order in Layer to 2, and provide it with a Polygon Collider 2D to enable interactions.
For the player's paddle, insert an image named "Paddle". Assign the "Player" tag to it, adjust its Order in Layer to 1, and attach a Polygon Collider 2D to define its collision boundaries.
Proceed by adding an image for the ball, labeled as "Ball". Tag it as "Ball" set its Order in Layer to 1, and equip it with both a Circle Collider 2D for collisions and a Rigidbody 2D for physics interactions. To prevent gravity's effect on the ball, modify the Rigidbody's Gravity property to 0.
With these components in place, you may have noticed that the game remains inactive if you hit the Play button. This is because we haven't yet implemented the necessary animations.
4. Add Padel Script:
Create a new PlayerScript.
Click on Assets, in Assets got to Scripts and then create PlayerScript.cs. Incorporate the script onto the paddle object. Proceed to modify the script accordingly. Initially, let's introduce a couple of variables to facilitate our operations.
public float playerVelocity = 0.3f;
private Vector3 playerPosition;
Next, it's imperative to acquire the initial position of the paddle, a task allocated to the Start function.
playerPosition = gameObject.transform.position;
Lastly, our focus shifts to enhancing the Update function with code responsible for paddle movement.
// horizontal movement
playerPosition.x += Input.GetAxis("Horizontal") * playerVelocity;
// update the game object transform
transform.position = playerPosition;
Upon initiating the Play mode and experimenting with arrow key inputs for paddle manipulation, it becomes evident that the paddle can pass through the walls.
To rectify this, a solitary variable is required, given that the paddle's design places it midway between the two walls. The numerical value to input in this variable could vary based on the specific context.
public float boundary = 6.52f;
Within the Update function, the definition of the minimum and maximum distances for the paddle completes this correction process, effectively preventing the paddle from exceeding these boundaries.
if (playerPosition.x < -boundary)
{
transform.position = new Vector3(-boundary, playerPosition.y, playerPosition.z);
}
if (playerPosition.x > boundary)
{
transform.position = new Vector3(boundary, playerPosition.y, playerPosition.z);
}
5. Add Escape Mode:
To enable users to halt the game, they can press the ESC key. Generate a fresh script labeled "WallScript" and attach it to the Wall object. Within the WallScript, establish a private function (which will be reused later) and invoke it within the context of the Update function.
private void QuitApp()
{
// using the platform dependent commands
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#elif UNITY_WEBGL
//if fullscreen is true, turn it to false
if(Screen.fullScreen == true)
{
Screen.fullScreen = !Screen.fullScreen;
}
//open an URL when quitting WebGL
Application.OpenURL("https://mountainpath.ch/cmsimplexh/index.php?Unity-3D/Create-a-breakout-game");
#else
Application.Quit();
#endif
}
// leave the game
if (Input.GetKeyDown(KeyCode.Escape))
{
QuitApp();
}
6. Make Animation of Ball:
To instigate the ball's bouncing behavior, we require certain physics adjustments. Generate a fresh Physics Material 2D, labeling it as "BallPhysics". Within the Inspector, set the Friction value to 0 and the Bounciness value to 1. Subsequently, associate the "BallPhysics" material with the ball's Circle Collider 2D via the Material property.
Design a new script called "BallScript" and attach it to the ball. Now, let's outline the ball's intended actions:
To accomplish these tasks, we need to define four variables:
private int ballIsActive;
private Vector3 ballPosition;
private Vector2 ballInitialForce;
private float OriginY;
We initialize stuff in Start function:
// create the force
ballInitialForce = new Vector2(100.0f, 300.0f);
// set to inactive
ballIsActive = 0;
// ball position
ballPosition = transform.position;
// get Y position of ball used for reset
OriginY = transform.position.y;
We add in Update function:
// check for user input
if (Input.GetButtonDown("Jump") == true)
{
// check if is the first play
if (ballIsActive == 0)
{
// add a force
GetComponent<Rigidbody2D>().AddForce(ballInitialForce);
// set ball active
ballIsActive = 1;
}
}
Initiate the Play mode to evaluate the ball launch by pressing the spacebar. Currently, the ball doesn't remain attached to the paddle, necessitating a remedy.
//Ball reset (falls off-screen)
if (ballIsActive == 1 && transform.position.y < -6)
{
//set the ball to inactive
ballIsActive = 0;
//get the paddle X coordinate
ballPosition.x = playerObject.transform.position.x;
//get the ball's origin Y coordinate
ballPosition.y = OriginY;
//set the ball to the paddle
transform.position = ballPosition;
}
Introduce a new variable to facilitate this alignment.
private int BIForce = 0;
Within the Unity environment, establish the linkage between the paddle and the "BallScript"
Commence the Play mode and observe that the ball now tracks the paddle's movements. However, its failure to reset upon falling off-screen remains unresolved.To address this, incorporate the following code snippet within the Update function:
//Ball reset (falls off-screen)
if (ballIsActive == 1 && transform.position.y < -6)
{
//set the ball to inactive
ballIsActive = 0;
//get the paddle X coordinate
ballPosition.x = playerObject.transform.position.x;
//get the ball's origin Y coordinate
ballPosition.y = OriginY;
//set the ball to the paddle
transform.position = ballPosition;
}
Following this adjustment, activate the Play mode again to evaluate the reset functionality. However, a noticeable issue arises: the ball's speed appears to be influenced after each restart from the paddle. This discrepancy can be attributed to the continuous addition of force without a reset to zero.
To mitigate this, introduce a new variable. Subsequently, modify the GetComponent call within the Update function to ensure that the force is added only once:
// add it only once, otherwise it goes to fast
if (BIForce == 0)
{
// add a force
GetComponent<Rigidbody2D>().AddForce(ballInitialForce);
BIForce = 1;
}
Proceed to activate the Play mode and test the reset feature. You'll observe that the ball now maintains a consistent speed after each restart from the paddle.
7. Add Block Script:
Generate a fresh script named "BlockScript". Incorporate this script into the paddle. Now, proceed to modify the script, involving the incorporation of three variables.
public int hitsToDestroy = 1;
//block's point value
public int points = 10;
//hit count
private int numberOfHits;
Under Start:
//initialize the number of hits
numberOfHits = 0;
//initialize the number of hits
numberOfHits = 0;
We use the predefined OnCollisionEnter2D function:
//On objects collision do this
void OnCollisionEnter2D(Collision2D collision)
{
//check if collision with ball happens
if (collision.gameObject.tag == "Ball")
{
//add 1 to the number of hits
numberOfHits++;
//check if we have the max number of hits
if (numberOfHits == hitsToDestroy)
{
// destroy the object
Destroy(this.gameObject);
}
}
}
Play the game to test it.
8. Create Block Prefab:
To streamline the process of duplicating blocks, we'll generate prefabs from the current BBlue block.
9. Level Creation:
Simplify the level design process by following these steps:
10. Score Sytem:
We need a game object that remains the same throughout playtime in order to manage the lives and score. One would grab the paddle, but if it changes (for instance, as a result of a bonus), all is lost. We'll use the walls for this tutorial.
Insert two variables into the WallScript.
//number of lives
private int playerLives;
//number of points
private int playerPoints;
Initialize variables in start:
//give 3 lives
playerLives = 3;
//give 0 points
playerPoints = 0;
Add a function to count points:
领英推荐
void addPoints(int points)
{
playerPoints += points;
}
The block's points must be added to the counter if it is eliminated (struck by the ball). We shall send a message from the block to the wall instead of connecting all the blocks to it because there are too many.
Go to the OnCollisionEnter2D function in the BlockScript and paste the following code before the Destroy() command:
// get reference of wall object
GameObject wall = GameObject.FindGameObjectsWithTag("Wall")[0];
// send message
wall.SendMessage("addPoints", points);
Back in the WallScript we add some GUI objects to show all the needed information in a function:
void OnGUI()
{
//create a box object
GUI.Box(new Rect(Screen.width / 2 - 260, 0,Screen.width / 2 + 180, 25)," ");
//create the label inside the box
GUI.Label(new Rect(Screen.width / 2 - 255, 3, Screen.width / 2 + 175, 22), "Arrows to move. Spacebar to launch. Type ESC to leave. *** Lives: " + playerLives + " Score: " + playerPoints);
}
In the same script we add a counter.
void TakeLife()
{
playerLives--;
}
In the BallScript, where the ball reset stuff happens, we need to add a SendMessage command to remove one live. Add this code:
// get reference of wall object
GameObject wall = GameObject.FindGameObjectsWithTag("Wall")[0];
// Send Message
wall.SendMessage("TakeLife");
If no live is available (playerLives = 0), then we restart the level.
For this we need to add the SceneManagement to the beginning of the WallScript.
using UnityEngine.SceneManagement;
We need also a new function that tests the number of lives:
//Handle the event "no more lives"
void WinLose()
{
// restart the game
if (playerLives == 0)
{
SceneManager.LoadScene("Level001");
}
}
This test is fired in the Update part:
// Check game state WinLose();
Hit Play and test your game!
11. Add Background Music:
To create a seamless gameplay experience, the background music will persist until the player exits the level. To achieve this, we shall link the music to the background image.
Here's how you can set it up:
By associating the music with the background image in this manner, you'll create an immersive atmosphere for players that remains consistent throughout the level.
12. Enhanced Background Music:
To ensure a flexible audio experience, we shall set up the background music to play until the player either leaves the level or manually toggles it off using a button. Here's how you can achieve this by linking the music to the background image:
AudioSource m_MyAudioSource;
//Play the music
bool m_Play;
//Detect when you use the toggle, ensures music isn’t played multiple times
bool m_ToggleChange;
// Start is called before the first frame update
void Start()
{
//Fetch the AudioSource from the GameObject
m_MyAudioSource = GetComponent<AudioSource>();
//Ensure the toggles are set to true for the music to play at start-up
m_Play = true;
m_ToggleChange = true;
}
// Update is called once per frame
void Update()
{
//Check to see if you just set the toggle to positive
if (m_Play == true && m_ToggleChange == true)
{
//Play the audio you attach to the AudioSource component
m_MyAudioSource.Play();
m_ToggleChange = false;
}
//stops the music if on
if (Input.GetKeyDown(KeyCode.Alpha1) && m_Play == true)
{
m_Play = false;
m_MyAudioSource.Stop();
}
//starts the music if off
if (Input.GetKeyDown(KeyCode.Alpha2) && m_Play == false)
{
m_Play = true;
m_ToggleChange = true;
}
}
13. Add Sound Effects:
To the ball and the wall, we will add some sound effects (the sound effects should actually play while losing a life and gaining points).
Uncheck all boxes and then add an audio source to the ball.
Add an Audio Source to the Wall and clear all the boxes first.
// link the sfx
public AudioClip hitSound;
Add a function to play on every collision:
void OnCollisionEnter2D(Collision2D collision)
{
if (ballIsActive ==1)
{
GetComponent<AudioSource>().PlayOneShot(hitSound);
}
}
Don’t forget to link the sound file to the variable in the inspector.
In the WallScript, add two variables:
//link the sfx
public AudioClip pointSound;
public AudioClip lifeSound;
And modify these two functions:
//add points to existing points
void addPoints(int points)
{
playerPoints += points;
GetComponent<AudioSource>().PlayOneShot(pointSound);
}
//remove live from total lives
void TakeLife()
{
playerLives--;
GetComponent<AudioSource>().PlayOneShot(lifeSound);
}
14. Create Bonus Prefab:
We must first add 4 sprites to the hierarchy. An animation will be generated automatically. Give the thing the name Bonus1. Set the tag to Bonus and add the Rigidbody 2D and Circle Collider 2D components. Incorporate a fresh BonusScript into the object. Change the script. Add these elements:
//Bonus' inital force
private Vector2 bonusInitialForce;
//Assign a point value to bonus
private int BonusPoints;
//Name of the bonus object
private string ObjName;
Initialize force in start function.
// create the force
bonusInitialForce = new Vector2(0f, -100.0f);
GetComponent<Rigidbody2D>().AddForce(bonusInitialForce);
Delete the bonus, within the Update function, if it falls off-screen.
//Bonus object delete when missed by player (falls off-screen)
if (transform.position.y < -6)
{
//Debug.Log("BonusScript - missed bonus removed!");
Destroy(this.gameObject);
}
The bonus's potential collision with the paddle is the next action to take. If this occurs, we take the appropriate action (in this case, we add some points) and remove the bonus object.
Add the subsequent feature:
void OnCollisionEnter2D(Collision2D collision)
{
// bonus catched by player
if (collision.gameObject.tag == "Player")
{
// get the name of the cloned object
ObjName = this.name;
ObjName = ObjName.Substring(0, 6);
//Debug.Log("BonusScript - Got a bonus: " + ObjName); //ObjName returns "Bonus2"
// test what bonus was caught and what to do
if (ObjName == "Bonus1")
{
BonusPoints = 10;
}
if (ObjName == "Bonus2")
{
BonusPoints = 10;
}
if (ObjName == "Bonus3")
{
BonusPoints = 10;
}
if (ObjName == "Bonus4")
{
BonusPoints = 10;
}
if (ObjName == "Bonus5")
{
BonusPoints = 10;
}
if (ObjName == "Bonus6")
{
BonusPoints = 10;
}
// get reference of player object
GameObject wall = GameObject.FindGameObjectsWithTag("Wall")[0];
// send message
wall.SendMessage("addPoints", BonusPoints);
// destroy the bonus object if caught
Destroy(this.gameObject);
}
}
The bonus functionality is integrated within the block, necessitating an update to the BlockScript.
Add these variables:
// set the bonus value
public int Bonus = 0;
public Transform MyBonusPrefab;
When a block has no bonus, which is the default condition, the Bonus variable is assigned the value of 0. This configuration eliminates the need to modify the existing block prefabs.
To implement the bonus creation process, specific code must be incorporated within the OnCollisionEnter2D function, immediately prior to the block's removal. This code will facilitate the generation of a bonus when the block is successfully hit by the ball and subsequently marked for deletion.
// drop bonus object
if (Bonus != 0)
{
Instantiate(MyBonusPrefab, gameObject.transform.position, MyBonusPrefab.rotation);
}
To verify the effectiveness of this mechanism, return to the Unity environment. Choose a block element and navigate to the Block Script section. Within this section, set the Bonus variable to 1 and proceed to assign a bonus prefab to the designated "My Bonus Prefab" slot.
Hit Play and test!
15. Adding Levels:
To generate a fresh level, initiate the duplication of the current setup. In the Inspector panel, facilitate alterations such as replacing the background image and modifying the music file. It's also recommended to customize the blocks to bring uniqueness to the new level.
As part of this process, ensure that you integrate these new levels into the Build Settings menu for seamless access.
In a previous section, we established the WinLose function within the WallScript, which previously aided in restarting at level 1 if all the balls were depleted. To further enhance this mechanism, we are extending the function's scope to account for scenarios where all blocks are successfully eliminated.
Incorporate the provided code snippet into the existing WinLose function to effectively cater to this situation. This enhanced functionality will enable a more comprehensive and rewarding gameplay experience.
Add this code to the WinLose function.
// all blocks destroyed
if ((GameObject.FindGameObjectsWithTag("Block")).Length == 0)
{
// check the current level
if (SceneManager.GetActiveScene().name == "Level001")
{
SceneManager.LoadScene("Level002");
}
else
{
QuitApp();
}
}
16. Improve Levels Loading:
The subsequent level loading process is managed by the WallScript. However, the current code structure demands expansion for each new level, which is not an optimal approach.
To address this, let's enhance the code within the "if" condition.
((GameObject.FindGameObjectsWithTag("Block")).Length == 0)
The objective is to introduce a check to determine if the final level has been reached. Should this be the case, the game will transition back to the main menu. Conversely, if the final level is not attained, a mechanism will be employed to dynamically ascertain the name of the subsequent level, allowing for its seamless loading. This
// check if we are at the last level
if (SceneManager.GetActiveScene().name == "Level002")
{
//if the last level is achieved, go back to main menu
QuitLevel();
} else
{
//Save all variable values
SaveStats();
//load next level
//get the current level name e.g. Level001
curLevelName = SceneManager.GetActiveScene().name;
//convert the string number to int e.g. 001 to 1
int.TryParse(curLevelName.Substring(5, 3), out curLevelNumber);
//add 1 to level number, e.g. 1+1=2
curLevelNumber++;
//create the new level name, e.g. Level002
if (curLevelNumber < 10)
{
nextLevelName = "Level00" + curLevelNumber.ToString();
} else if (curLevelNumber < 100)
{
nextLevelName = "Level0" + curLevelNumber.ToString();
} else
{
nextLevelName = "Level" + curLevelNumber.ToString();
}
SceneManager.LoadScene(nextLevelName);
}
Hit Play to test!
17. Gameover Screen:
To trigger the gameover screen upon either completing the game or losing all available balls, follow these instructions:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameOBackScript : MonoBehaviour
{
public void ReturnMainMenu()
{
SceneManager.LoadScene(0);
}
}
Link the BackButton with the script in the On Click () section.
Create the GameoverScript and add it to the GameoverPanel.
In the header add:
using UnityEngine.UI;
Add these variables:
//link the text objects
public Text LoserText;
public Text WinnerText;
//var for gameover state
private int GameoverState;
Add this code in the Start function:
//get the gameover state from memory (10=no lives, 20=win game) GameoverState = PlayerPrefs.GetInt("gameover");
if (GameoverState == 10)
{
//if no more lives
LoserText.gameObject.SetActive(true);
}
else if (GameoverState == 20)
{ //if win the game
WinnerText.gameObject.SetActive(true);
}
In Unity, link the two text objects to the script.
Now we have to write the gameover state to memory from within the WallScript.
In the WinLose() function, in the if (playerLives == 0) part, add the following:
//store the gameover var (10=no lives, 20=win game) PlayerPrefs.SetInt("gameover", 10);
In the WinLose() function, in the if (SceneManager.GetActiveScene().name == LastLevel) part, add the following:
//store the gameover var (10=no lives, 20=win game) PlayerPrefs.SetInt("gameover", 20);
Hit Play to test!
If you like the article please ?? it, wants to refer somebody ?? with him/her. We also provide Services of 2D/3D Game Development, 2D/3D Animations,Video Editing, UI/UX Designing.
If you have questions or suggestions about the game or want something to build from us, Feel free to reach out to us anytime!
?? Mobile: +971 544 614 238
?? Email: [email protected]