In this demo, we will create our own Flappy Bird game using Unity and C#.
- Unity is, essentially, a well-rounded game engine that truly does simplify game development. While there may be better engines to choose from, learning Unity will only help you grow as a game developer.
For this project, we are using the 2020.3 LTS table release. To create this project you will need to have Unity and Unity Hub installed. You can download them from here. To get the completed project, you can download it from the link below. (Link is available after successful registration)
First, we open the Unity Hub and Create a 2D Project.
We create a Sprites folder in the game directory, and we import all the needed images for the game.
- After we import the needed images, you can see that you have more than one bird. We slice it save it in a new folder called Animations.
- We set it on the scheme, we can start the application and see if the bird will fall.
- What we need to do next is create a script for our bird. We create a Bird.cs file in the Assets/Scripts folder. We create methods for the Bird to fly and set boundaries so it cannot fly outside our screen.
using System;
using UnityEngine;
public class Bird : MonoBehaviour
{
[SerializeField] private Rigidbody2D _rigidbody;
[SerializeField] private float _force;
[SerializeField] private float _yBound;
private void Update()
{
if (Input.GetMouseButtonDown(0) && _rigidbody.position.y < _yBound)
{
Flap();
}
}
private void Flap()
{
_rigidbody.velocity = Vector2.zero;
_rigidbody.AddForce(Vector2.up * _force);
}
}
- Now we need to create pipes. We create Upper and Lower pipes. If the bird hits them the game will be over.
- After we are done we create a MovingObject infinity runner. We need to constantly move the objects to the left. The speed increased by one, for every second that passes. We set boundaries for when the object is destroyed. We attach the script to the pipe. We can check if our scripts work.
using UnityEngine;
public class MovingObject : MonoBehaviour
{
[SerializeField] private float _speed;
[SerializeField] private float _xBound;
private void Update()
{
this.transform.position -= Vector3.right * _speed * Time.deltaTime;
if (this.transform.position.x < _xBound)
{
Destroy(this.gameObject);
}
}
}
- We can move to the spawner script. We create the class in the same scripts folder. We need a prefab to be spawned, and a time interval arrange by 1 axis with a name – _yClamp. If enough time has elapsed, the object will spawn, and the elapsed time will reset.
using System;
using UnityEngine;
public class Spawner : MonoBehaviour
{
[SerializeField] private GameObject _prefab;
[SerializeField] private float _time;
[SerializeField] private float _yClamp;
private float _elapsedTime;
private void Update()
{
_elapsedTime += Time.deltaTime;
if (_elapsedTime > _time)
{
SpawnObject();
_elapsedTime = 0f;
}
}
private void SpawnObject()
{
float offsetY = UnityEngine.Random.Range(-_yClamp, _yClamp);
Vector2 pos = new Vector2(this.transform.position.x, this.transform.position.y + offsetY);
Instantiate(_prefab, pos, Quaternion.identity, this.transform);
}
}
- After we are done we can put the spawner into action. We create a spawner to spawn pipes, evey 2 seconds in the y-axis.
- We add the functionality to the ground as well. If we hit play we can see the pipes and ground spawning infinitely.
- In our next step, we need to make the game end. We add events to the Bird inside the Bird.cs class. As a bonus you can add sounds for different events. With OnCollisionEnter2D, our bird dies and we freeze the time.
[SerializeField] private AudioSource _audioSource;
[SerializeField] private AudioClip _flapSound;
[SerializeField] private AudioClip _hitSound;
[SerializeField] private AudioClip _scoreSound;
public static event Action OnDeath;
private void OnCollisionEnter2D()
{
OnDeath?.Invoke();
_audioSource.PlayOneShot(_hitSound);
Time.timeScale = 0f;
}
private void Start()
{
Time.timeScale = 1f;
}
We need to create a button, which starts our game.
- We use the Observer Design Pattern and it’s commonly used in game development.
We create a new class called UIManager. In our class we add the following play button logic:
[SerializeField] private GameObject _playButton;
[SerializeField] private TMP_Text _score;
private void Awake()
{
Bird.OnDeath += OnGameOver;
}
private void OnDestroy()
{
Bird.OnDeath -= OnGameOver;
}
public void RestartGame() => SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
private void OnGameOver() => _playButton.SetActive(true);
- What we need to do is create a text mesh for the score on our screen. In the Bird class, we add functionality so that the score will change. The OnTrigger event will trigger and play a sound.
public static event Action OnScore;
private void OnTriggerEnter2D()
{
OnScore?.Invoke();
_audioSource.PlayOneShot(_scoreSound);
}
- In the UI Manager we also need to add the functionality for the score. Each time the bird passes a pipe depending on the state we will get Awake() or OnDestroy() event. We increment the current number of the score when the score increases.
private void Awake()
{
Bird.OnDeath += OnGameOver;
Bird.OnScore += OnScore;
}
private void OnDestroy()
{
Bird.OnDeath -= OnGameOver;
Bird.OnScore -= OnScore;
}
private void OnScore() => _score.text = (int.Parse(_score.text) + 1).ToString();
If we followed all the steps as shown in the video tutorial, our project should be working. What you learned in this tutorial will work for most of your projects in any engine. Unity‘s pipeline is smooth, yet complex. Unity is, simply put, the world’s most popular game engine. It packs a lot of features together and is flexible enough to make almost any game you can imagine. Unity packs tools for 2D and 3D game development.
Lesson Topics
- Project Creation
- Project Setup
- Creating a Bird
- Adding the Bird Script
- Prefabs
- Adding Moving Object
- Defining Spawner
- Setup Spawners Usage
- Implementing Game Over Feature
- Adding Score
- Adding SFX
- Testing Project