Five years ago, the Flappy Bird madness took over the world of mobile applications. A simple 2D game made by an independent developer was ranked first in the Apple App Store and Google Play Store rankings. This success demonstrated that a well-designed simple game with strong addictive power could be a huge success. In this tutorial, I show you how to create a Flappy Bird clone for Android. It is the ideal tutorial to discover how to create a simple 2D Game for Android.
Simplicity can sometimes drive tosuccess. This is even more so when this simplicity is coupled with a strong addictive power. Indeed, it is this addictive side that is at the root of Flappy Bird’s phenomenal success in 2014. Flappy Bird’s gameplay is simple and based above all on the player’s agility. The latter must move a bird forward in a horizontally scrolling scenery by tapping on the touch screen of his mobile device.
The main difficulty of the game is that the player must avoid obstacles in the form of pipes at the top and bottom of the screen. Each time a pipe passes, the player scores a point. As the player progresses through the environment, he can collect coins to score additional points. The game only ends when the player touches a pipe or hits the ground represented by the bottom of the screen. Flappy Bird’s success also lies in its graphics, which are reminiscent of those of Super Mario World in particular. The game quickly becomes addictive or even frustrating when you start trying to compete with other players around the world.
Because of its simplicity, Flappy Bird is a perfect case study to learn how to create a first simple 2D Game for Android. As part of this tutorial, we will make a Flappy Bird clone without any external library. Thus, you could really discover the basics of creating a 2D Game. Our clone will be named Flying Bird.
Preparing Sprites and Sounds
The first step in the creation of our game Flappy Bird clone is to recover the graphic elements that are partly behind the success of Flappy Bird. In the world of game programming, these graphic elements are called Sprites. All our Sprites will be placed in the /res/drawable folder in the tree structure of our Android application project. Figure 1 gives an overview of all the Sprites that will be used by our Flappy Bird game implementation.
As you can see, there are:
- The game environment in day and night mode
- The ground
- The Sprite that will give the impression of horizontal scrolling with the pipes
- The numbers used to display the score
- The different Sprites of the bird, the pipes
- The Sprites used at the end of the game for example to display the rewards
Our clone cannot be realistic without reproducing the sound universe of the original game. To do this, we place the different sounds of the game within the assets folder. Among the different sounds we will use, we will find the sound emitted when the player scores a point or the bird dies.
Creating the User Interface
The user interface will mainly consist of a drawing area on which we will render the different dynamic elements of our game. Static elements can be defined within the layout of our main activity. The Flappy Bird background is the static element that we will define within an ImageView taking the entire screen. The dynamic part will be drawn as the game evolves within a SurfaceView component. This gives us the following layout for our game:
Besides, we need to define a particular theme for Flappy Bird so that it can be displayed in full screen. Our theme will inherit Theme.AppCompat.Light.NoActionBar allowing us not to display an action bar. To this theme, we add the android:windowFullscreen property set to true in order to tell the Android OS that our application must be displayed in full screen:
The theme is then applied at the application level within the Android Manifest as usual.
Initiating our Flappy Bird Game
When the main activity of the game is created, we retrieve the ImageView and SurfaceView components previously defined in the layout. Then, we call the setKeepScreenOn method with true as a parameter to specify that we want to keep the screen on during a game of Flappy Bird. We continue the configuration of our SurfaceView instance by defining that the main activity of the game, which implements the OnTouchListener interface, will receive the events triggered when a player touches the screen.
After that, we retrieve the SurfaceHolder object associated with our SurfaceView via a call to the getHolder method. This object allows us to interact with the drawing area of the SurfaceView by accessing its Canvas element. The initialization of the game continues by defining the pixel format for the SurfaceHolder as well as the implementation of the SurfaceHolder.Callback interface which should receive calls during events related to the lifecycle of the first named object. These events are the creation, destruction or change affecting the SurfaceHolder.
Finally, we finish by loading the various resources useful to Flappy Bird such as pipes or parts. We also create a SoundPool object instance that will then be used to manage the sound part of the game. We also take the opportunity to load the different sounds we will use during a game of Flappy Bird from the assets directory. To do this, the file of each sound is opened using the AssetManager before loading the contents of the file via the SoundPool load method. All this gives us the following initialization code:
The evolution of any game in computer is carried out within what is commonly called the Game Loop. As part of the implementation of our Flappy Bird game, we implement it by creating a Thread separated from the UI rendering Thread of the Android application. This separated Thread will be in charge of managing the Game Loop.
So, once the SurfaceHolder object is created, we start our dedicated Thread that we call DrawingThread. This Thread is stopped when the callback surfaceDestroyed is called telling us that the SurfaceHolder object instance is destroyed. This happens when the application is paused or stopped, for example.
In our DrawingThread, we loop as long as the Thread is active. First, we take care of the rendering of our game. To do this, we obtain a reference to the Canvas of our SurfaceHolder object by calling its lockCanvas method. We then empty the content of this Canvas before iterating on all the elements of our game that we want to return to the screen. These elements being our famous Sprites that we mentioned earlier.
This iteration is performed via an Iterator object and we use it to delete Sprites considered as no longer alive. This work is encapsulated within a try / finally block. In the finally part, we ask that the updates we have made on the Canvas be posted on the SurfaceHolder via a call to the unlockCanvasAndPost method with the current instance of Canvas passed as a parameter.
We then obtain a code with the following look:
You also have noticed that the rendering time is measured before comparing this time to a constant called GAP. This constant allows us to add, if necessary, a delay to avoid that the rendering phase of the Game Loop be too fast.
The Game Loop set up with the rendering phase of the game performed within a dedicated Thread, we can move on to the Sprites management. As these latter have in common a certain number of behaviors, we will create an interface to model these behaviors. The Sprite interface will thus propose an onDraw method charged to request the drawing on the Sprite’s Canvas, an isAlive method to know if a Sprite is still alive or not, an isHit method to manage the collision between a Sprite and another Sprite and finally a getScore method to return the number of points associated with a Sprite instance:
The isAlive method of a Sprite is useful in the Game Loop as we saw earlier to know if it should be returned to the screen or if it should be removed from the Sprites’ list.
We start by creating a first implementation of the Sprite interface. The BirdSprite class will be in charge of managing the bird in our game. Within the BirdSprite object builder, we load the different visual versions of the bird we will use. This allows the bird to change color as the player progresses. We also load the constants used to calculate the bird’s dimensions, its initial position on the screen, its acceleration or the position of the ground. This last constant allows us to detect if the bird has hit the ground in particular.
Most of the work is done within the onDraw method of the BirdSprite in which we first manage the bird’s color change via the count property. If the status of the Sprite object is different from the STATUS_NOT_STARTED constant, the current height position of the bird will be incremented with the current speed previously loaded.
This latter is then incremented with the acceleration intended to reproduce the effect of gravity on our bird. It is then ensured that the bird remains within the limits of the screen by resetting its current position to 0 if it reaches the top of the screen. The same is done for its position at the bottom of the screen. Finally, the Sprite associated with the bird is returned on the Canvas instance passed as the input of the onDraw method.
A last word about the implementation of the isHit method for which true will be returned if the current position of the bird exceeds that of the ground represented by the maxHeight property. In this case, the game will be over.
This gives us the following code for our BirdSprite class:
In addition to the BirdSprite, we add:
- a BlockerSprite to manage the pipes displayed on the screen that must be avoided by the player
- a CoinSprite to display the pieces to be collected
- a GroundSprite to manage the ground and its horizontal scrolling to give the illusion of the bird’s progress on the screen
These latter are built on the same principle with a constructor in which the Sprite is initialized before implementing the various methods of the Sprite interface including the onDraw method to return the Sprite to the screen.
We can now complete our Game Loop with game updates about Sprites. We will add the following code to detect if the game should stop because the player has lost:
Here, we go through the Sprites and if the BirdSprite has hit the ground or a pipe, we change the current game status to STATUS_GAME_OVER: the game is over and we will have to display the end screen to the player the next time the Game Loop passes. This screen is modeled within a GameOverSprite object.
Interacting with the Player
The goal of Flappy Bird is to allow the player to progress the bird by tapping on the screen, so we must act within the onTouch method of the OnTouchListener interface that our main activity inherits. It is in this method that we will interact with the player when they type on the screen. If the game has not yet started, a first tap on the screen will allow you to change its status to the STATUS_NORMAL constant, which will allow you to update the game elements within the Game Loop where the following condition is defined at the beginning of the update area:
In case the game is in progress, we call the onTap method of the BirdSprite to increment its current speed which causes the bird to rise on the screen. This rise on the bird’s screen fights against the effect of gravity by preventing it from falling to the ground.
Finally, if the game is over, we call the onTap method of the GameOverSprite object which will return a constant as output allowing us to know which part of the end screen the player to touch in order to react accordingly either to start a new game or to share the successful score via social networks for example. The implementation of the onTouch method therefore looks like this:
In the section on Sprite management, the isHit method has already been mentioned, clearly suggesting that it would be used to manage the obstacles that our dear bird must avoid throughout the game. For some Sprites, this method does not make sense and they will always return false to the call of this method. This is the case, for example, of a CoinSprite. On the other hand, this method makes sense for BlockerSprite objects representing the pipes that the bird must avoid.
We will therefore focus in more detail on the implementation of the isHit method within the BlockerSprite class. The isHit method takes as input a Sprite object that corresponds to the object for which we want to determine if it has collided with the current BlockerSprite. First we check that the Sprite passed at the entrance is indeed of the BirdSprite type since we only want to manage collisions between the bird and the pipes.
If so, the rectangle encompassing the bird is determined before comparing it to the position of the current pipe. If there is a collision, then true is returned. Within the Game Loop, this return allows us to know if the game can continue or if it is finished.
The code of the isHit method of the BlockerSprite is as follows:
Adding Sounds for our Flappy Bird Game
In order to make our Flappy Bird clone more attractive, it is essential to reproduce the sounds present in the original game. These sounds occur when the game starts, when the bird scores a point or when the user hits the screen and the bird gets taller.
We saw in the initialization part of the game that the sounds were loaded into an array of integers named soundIds and indexed with constants ranging from 0 to 4.
Each one representing a very particular sound to play during the Flappy Bird game. Sounds are played by calling the play method of the SoundPool object created at the beginning of the game. The emission of a sound when the bird hits a pipe will be done in this way within a dedicated onHit method:
It is worth noting here that the sound is emitted in the main Thread of the application. The call to the play method must therefore be embedded within the runOnUiThread method.
The phenomenal success of Flappy Bird is also due to the addictive side created by the competition between players around the world. This competition could only be generated by comparing scores between players. Managing scores in this type of game is therefore fundamental. As part of our Flappy Bird clone implementation, we will limit ourselves to storing scores locally by using the Preferences API offered by Android as standard.
In this sense, we create a Scores object proposing static methods to obtain the best score achieved and recording a new best score. Here is its code:
Our Flappy Bird Clone Game in Action
Once the code of our game is finished, it’s time to move on to the most pleasant part, namely testing our Flappy Bird clone in real conditions on Android smartphones. Figure 2 shows the initial screen of the game.
After a first touch on the screen, the game starts and you have to try to move the bird forward by avoiding the pipes while collecting as many pieces as possible to score as many points as possible (figure 3).
Finally, if the bird hits a pipe or touches the ground, the game is over and the end of game screen is displayed (Figure 4).
You can also watch our Flappy Bird Game Clone in Action on YouTube:
Through the creation of a clone of the famous Flappy Bird game, this article will have allowed you to discover how easy it is to create a small 2D game of the casual type for Android smartphones. Of course, a large number of libraries exist to facilitate the creation of this kind of game but it is always interesting to discover how they are made before using frameworks.
Now that you have acquired the basics of creating 2D Games under Android, you can move on to making your own game in the hope of creating, why not, the next Flappy Bird.
That’s all I wish you.