Build a simple Flappy Bird clone in Android Studio

build a simple Flappy Bird clone in Android Studio

Recap

For this post, the previously mentioned article and video should be considered required reading/viewing. To briefly recap, we built ourselves a canvas on which to draw our sprites and shapes, and we made a separate thread to draw to that without blocking up the main thread. This is our “game loop.”

We have a class called CharacterSprite which draws a 2D character and gives it some bouncy movement around the screen, we have GameView which created the canvas, and we have MainThread for the thread.

Go back and read that post to develop the basic engine for your game. If you don’t want to do that (well, aren’t you contrary?), you could just read through this to learn some more skills. You could also come up with your own solution for your game loop and sprites. For instance, you can achieve something similar with a custom view.

Making it flappy

In the update() method of our CharacterSprite class, there’s an algorithm to bounce the character all around the screen. We’re going to replace that with something much simpler:

y += yVelocity;

If you recall, we had defined yVelocity as 5, but we could change this to make the character fall faster or slower. The variable y is used to define the position of the player character, which means it will now fall slowly. We don’t want the character to move right anymore, because we’re going to be scrolling the world around ourselves instead.

This is how Flappy Bird is supposed to work. By tapping the screen, we can make our character “flap” and thereby regain some height.

As it happens, we already have an overwritten onTouchEvent in our GameView class. Remember, this GameView is a canvas shown in place of the usual XML layout file for our activity. It takes up the whole screen.

Pop back into your CharacterSprite class and make your yVelocity and your x and ycoordinates into public variables:

JAVA
public int x, y;
private int xVelocity = 10;
public int yVelocity = 5;

This means those variables will now  be accessible from outside classes. In other words, you can access and change them from GameView.

Now in the onTouchEvent method, simply say this:

characterSprite.y = characterSprite.y - (characterSprite.yVelocity * 10);

Now wherever we tap our canvas, the character is going to rise by ten times the speed at which it is falling each update. It’s important we keep this flappy-ness equivalent to the fall speed, so we can choose to change the force of gravity later and keep the game balanced.

I also added a few little touches to make the game a bit more Flappy Bird-like. I swapped out the color of the background for blue with this line:

canvas.drawRGB(0, 100, 205);

I also drew myself a new bird character in Illustrator. Say hello.

Obstacles!

Now we have a bird which falls to the bottom of the screen unless we tap to fly. With the basic mechanic sorted, all we need to do is to introduce our obstacles! To do that we need to draw some pipes.Since this is the same pipe facing both ways, it is possible to flip it by using the method we borrowed earlier (we just set height to a minus number). To keep things simple, we may as well use two separate images.

Now we need to create a new class and this class is going to work just like the CharacterSprite class. This one is going to be called “PipeSprite.” It’s going to render both pipes on the screen — one at the top and one at the bottom.

In Flappy Bird, pipes appear at different heights and the challenge is flapping the bird up to fit through the gap for as long as you can.

The good news is that a class can create multiple instances of the same object. In other words, we can generate as many pipes as we like, all set at different heights and positions and all using a single piece of code. The only challenging part is handling the math so we know precisely how large our gap is! Why is this a challenge? Because it needs to line up correctly regardless of the size of the screen it’s on. Accounting for all this can be a bit of a headache, but if you enjoy a challenging puzzle, this is where programming can actually get quite fun. It’s certainly a good mental workout!

Now, it would be tempting to do all this math ourselves and just “know” our gap is 500p, but that’s bad programming. It means we’d be using a “magic number.” Magic numbers are arbitrary numbers used throughout your code which you are expected to just remember. When you come back to this code in a year’s time, will you really remember why you keep writing -250 everywhere?

Instead we’ll make a static integer – a value that we won’t be able to change. We call this gapHeight and make it equal to 500. From now on, we can refer to gapHeight or gapHeight/2 and our code will be much more readable. If we were being really good, we’d do the same thing with our character’s height and width too.

 Also Read:Top tips to learning Android development easy

Place this in the GameView method:

JAVA
public static int gapHeigh = 500;

While you’re there, you could also define the speed at which the game will play:

JAVA
public static int velocity = 10;

You also have the option to turn that gapHeight variable into a regular public integer, and have it get smaller as the game progresses and the challenge ramps up — Your call! The same goes for the speed.

With all this in mind, we can now create our PipeSprite class:

JAVA
public class PipeSprite {

    private Bitmap image;
    private Bitmap image2;
    public int xX, yY;
    private int xVelocity = 10;
    private int screenHeight = 
  Resources.getSystem().getDisplayMetrics().heightPixels;

    public PipeSprite (Bitmap bmp, Bitmap bmp2, int x, int y) {
        image = bmp;
        image2 = bmp2;
        yY = y;
        xX = x;
    }


    public void draw(Canvas canvas) {
        canvas.drawBitmap(image, xX, -(GameView.gapHeight / 2) + yY, null);
        canvas.drawBitmap(image2,xX, ((screenHeight / 2) 
  + (GameView.gapHeight / 2)) + yY, null);


    }
    public void update() {

        xX -= GameView.velocity;
    }

}

The pipes will also move left on each update, at the speed that we have decided for our game.

Back in the GameView method, we can create our object right after we create our player sprite. This happens in the surfaceCreated() method but I’ve organized the following code into another method called makeLevel(), just to keep everything nice and tidy:

JAVA
Bitmap bmp;
Bitmap bmp2;
int y;
int x;
bmp = getResizedBitmap(BitmapFactory.decodeResource
  (getResources(), R.drawable.pipe_down), 500, 
  Resources.getSystem().getDisplayMetrics().heightPixels / 2);
bmp2 = getResizedBitmap(BitmapFactory.decodeResource
  (getResources(), R.drawable.pipe_up), 500, 
  Resources.getSystem().getDisplayMetrics().heightPixels / 2);

pipe1 = new PipeSprite(bmp, bmp2, 0, 2000);
pipe2 = new PipeSprite(bmp, bmp2, -250, 3200);
pipe3 = new PipeSprite(bmp, bmp2, 250, 4500);

This creates three pipes in a row, set at different heights.

The first three pipes will have the exact same position each time the game starts, but we can randomize this later.

If we add the following code, then we can make sure the pipes move nicely along and are redrawn just like our character:

JAVA
public void update() {
        characterSprite.update();
        pipe1.update();
        pipe2.update();
        pipe3.update();
    }

    @Override
    public void draw(Canvas canvas)
    {

        super.draw(canvas);
        if(canvas!=null) {
            canvas.drawRGB(0, 100, 205);
            characterSprite.draw(canvas);
            pipe1.draw(canvas);
            pipe2.draw(canvas);
            pipe3.draw(canvas);

        }
    }

There you have it. There’s still a little way to go, but you just created your first scrolling sprites. Well done!

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: