Android

Flappy Bird Unity tutorial for Android – Full game in 10 minutes!


The wonderful thing about the current age of mobile technology is anyone can now become a successful developer. Not since the days of the ZX Spectrum have lone developers been able to create and distribute hit applications capable of going toe-to-toe with the output of big publishers as well as they can now.

Few things exemplify this more than the case of Flappy Bird. Flappy Bird was a very straightforward game developed by 28-year old Dong Nguyen under his company name dotGEARS. The mechanics and graphics couldn’t have been simpler, but it went on to earn $50,000 a day. It’s a fascinating story that you can read all about at RollingStone.

The point is: the app was nothing special. It was just in the right place at the right time and, with luck on its side, it made the creator rich. This can still happen today — you just need the right idea.

To demonstrate just how easy building something like this is, I’m going to show you how you can make your own Flappy Bird game in just 10 minutes. I discussed how to do this in Android Studio already, which was admittedly a bit more involved (though still pretty quick). I also discussed how you could make a 2D platformer in Unity in 7 minutes — though that was really only a basic framework.

But when you combine the ease of Unity, with the simplicity of Flappy Bird — well, that really is a 10 minute job.

The player character

First, create a new project, making sure to have 2D selected.

Drop your Flappy Bird sprite into your scene. I created one earlier for the last project, so I’ll use that again. Feel free to use the one you made too!

Once the sprite is in your scene, resize it to your liking by dragging the corners. It should also now be visible in your “Hierarchy” window on the left. This shows you all the objects in your “scene” and at this point, there should just be two: the camera and the bird.

Drag the camera in this view onto the bird and then release. It should now appear underneath the bird, which means it is now a “child” of the bird. This means the camera’s position will remain constant in relation to the bird. If our bird moves forward, the view will move with it.

Select the bird again in either the scene view or the hierarchy. You’ll see a list of options and attributes on the right in a view labelled Inspector. This is where you can manipulate the specific variables relating to that object.

Head down to the bottom and select Add Component. Now choose Physics2D > Rigidbody2D. This is a nice, ready-made set of instructions that will apply gravity to our player. Click on Constraints in this panel and then choose freeze rotation Z. This will prevent your birdy from spinning around like a madman and bringing the camera with him, which could get pretty nauseating pretty quickly.

Add a Polygon Collider in the same way, which will tell Unity where the edges of the character are. Click Play and the character sprite should now fall infinitely, bringing the camera with it.

So far so good!

We also want our character to be able to fly, but that is easy enough to implement.

First we need to create a C# script. Create a folder for that to go in (right click anywhere in assets to make a folder called “Scripts”) and right click and select Create > C# Script.

I called mine “Character.” Double click on it to open your C# editor, which might be MonoDevelop or Visual Studio. Now, add the following code:

public class Character : MonoBehaviour {

    public Rigidbody2D rb;
    public float moveSpeed;
    public float flapHeight;

    // Use this for initialization
    void Start () {
        rb = GetComponent<Rigidbody2D>();
    }
      

       // Update is called once per frame
       void Update () {
        rb.velocity = new Vector2(moveSpeed, rb.velocity.y);
        if (Input.GetMouseButtonDown(0))
        {
            rb.velocity = new Vector2(rb.velocity.x, flapHeight);
        }

        if (transform.position.y > 18 || transform.position.y < -19)
        {
            Death();
        }

    }

    public void Death()
    {
        rb.velocity = Vector3.zero;
        transform.position = new Vector2(0, 0);
    }

}

This code does two things. It keeps the player constantly moving forward at a speed we’ll be able to define in the inspector and it adds our “flapping” ability. The Update() method is called repeatedly as your game runs, so anything you place in here will occur continuously. In this case, we are adding a little velocity to our rigid body. Rb is the physics script (RigidBody2D) we applied to our object earlier, so when we say rb.velocity, we refer to the speed of the game object.

A mouseclick is interpreted by Unity as a tap anywhere on the screen if you’re using a mobile device. When we detect that, we make the character move up slightly.

The public float moveSpeed will control the speed of the movement and the public float flapHeight will handle the height increase of the bird each time we click. Because these variables are public, we’ll be able to change them from outside the script.

Death()is a public method. This means it’s a collection of code pertaining to our character which other scripts and objects will be able to invoke. It simply returns the position of our player to the start point. We’ll also use it each time the character goes too high or too low. You’ll see why this needs to be public in a moment. The rb.velocity = Vector3.zero; line is there to kill all momentum — so that our character doesn’t start falling faster and faster each time they restart at the beginning.

Come out of your editor and add the script as a component to your character (select the bird, choose Add Component > Scripts > Character). You’ll now be able to define the moveSpeed and flapHeight in the inspector (that’s what a public variable does). I set mine to 3 and 5 respectively, which seems about right.

One more thing: in the inspector you will also want to add a tag to your character. Click where it says Tag: Untagged and then choose Player from the drop-down list.

Obstacles

Next, we’ll add some obstacles: pipes. One man’s tunnel to hidden mushrooms is another man’s mortal foe.

Drag and drop a pipe sprite into your scene roughly where you’d like the first obstacle to go and call it pipe_up.

Now create a new script, also just as before, and call it “Pipe.” Here’s how that looks:

public class Pipe : MonoBehaviour {

    private Character character;

    // Use this for initialization
    void Start () {
        character = FindObjectOfType<Character>();
    }

      
       // Update is called once per frame
       void Update () {
        if (character.transform.position.x - transform.position.x > 30)
        {

        }
    }

    void OnCollisionEnter2D(Collision2D other)
    {
        if (other.gameObject.tag == "Player")
        {
            character.Death();  
        }
    }

}


Add this script to the pipe sprite in the same way as you did before. This will show when the pipe moves off the left of the screen. We haven’t actually put anything here yet, but we will be coming back to it.

OnCollisionEnter2D is a method called whenever your collider makes contact with another collider. In this case: when the player hits the pipe. The Death() method we created earlier is then called, forcing our player character back to the start point.

Now you have one pipe which will occasionally disappear and reappear at the other end of the screen. If you touch it, you die!

Upside-down pipes

You’re only going to have one upright pipe for now.

Now add another sprite. You can do this by right-clicking in the hierarchy and saying New 2D Object > Sprite and then selecting the sprite you want to use; it’s easier to just drag and drop the file into the scene again.

Rename this one: pipe_down. Where it says Draw Mode in the inspector, tick the box that says Flip: Y. As you might have guessed, this has now turned our sprite upside down. Add the same RigidBody2D.

Then create another new C# script, this time called PipeD. This is going to contain pretty much the same code:

public class PipeD : MonoBehaviour {

    private Character character;

    // Use this for initialization
    void Start()
    {
        character = FindObjectOfType<Character>();
    }

    // Update is called once per frame
    void Update()
    {
        if (character.transform.position.x - transform.position.x > 30)
        {
        }

    }

    void OnCollisionEnter2D(Collision2D other)
    {
        if (other.gameObject.tag == "Player")
        {
            character.Death();
        }

    }

}

If we were making a more involved game, we’d probably make a script called Hazard that made anything hurt the player and a separate script called Regen to have the obstacle refresh itself when the player went too far to the right.

Prefab sprout

Now, we could make our entire Flappy Bird game with just this bit of code. We could move the pipes to the right of the screen each time they disappeared, or copy and paste as many pipes as we wanted around the screen.

Were we to go with the first option, making sure the pipes lined up nicely when they were generated randomly and keeping things fair would be difficult. When the character died, they could respawn miles away from the first pipe!

If we chose the latter option — copying and pasting — we’d be using up a lot of memory unnecessarily, slowing down our game, and limiting replayability (because it would be the same every time!).

Instead, lets use what are known as “prefabs.” This is short for prefabricated, and it basically means we’re going to turn our pipes into templates we can then use to effectively produce more pipes at will. For the programmers among you, the pipe script is our class and each pipe on the screen is just an instance of that object.

To do this, just create a new folder called Prefabs. Now drag your pipe_up and pipe_down out from the hierarchy and into the folder.

Any time you drag and drop an object from your prefab folder, it will have the same properties, meaning you won’t need to keep adding components. More importantly, it means editing the size of the prefab in the folder will impact the size of the pipes throughout your game – no need to change them all individually.

As you can imagine, this has a lot of benefits from an organizational, time-saving point of view. It also means we can interact with our objects from within our code. We can create “instances” of our pipes.

First add this code into the if statement we left empty in our first pipe script’s update() method:

void Update () {
        if (character.transform.position.x - transform.position.x > 30)
        {

            float xRan = Random.Range(0, 10);
            float yRan = Random.Range(-5, 5);

            Instantiate(gameObject, new Vector2(character.transform.position.x + 15 + xRan, -10 + yRan), transform.rotation);
            Destroy(gameObject);

        }

    }

This is going to first “instantiate” our gameObject. Instantiating creates a new identical copy. In Unity, whenever you use the word gameObject, it refers to the object to which the script is currently attached — in this case our pipe.

We’re regenerating said pipe with slight random variations in the interest of fun.

But rather than doing the same thing in the PipeD script, we’re generating both objects in the same place. That way, we can easily keep the position of the second pipe relative to this first one. This also means we need less code for PipeD.

Create a public gameObject called pipeDown. Then update the code like this:

if (character.transform.position.x - transform.position.x > 30)
        {

            float xRan = Random.Range(0, 10);
            float yRan = Random.Range(-5, 5);
            float gapRan = Random.Range(0, 3);

            Instantiate(gameObject, new Vector2(character.transform.position.x + 15 + xRan, -11 + yRan), transform.rotation);
            Instantiate(pipeDown, new Vector2(character.transform.position.x + 15 + xRan, 12 + gapRan + yRan), transform.rotation);
            Destroy(gameObject);

        }

I also added in a gapRan variable which will allow us to slightly vary the size of the gap between the two pipes, just to keep things interesting.

Now hop back into Unity and drag the pipe_down prefab from the prefabs folder (important!) into the space where it says ‘Pipe Down’ (notice how it translates our camel case by inserting the space) on the pipe up sprite. Remember, we set Pipe Down to be a public gameObject, meaning that we can define what this object is from elsewhere – through the inspector in this case. By choosing the prefab for this object, we ensure that when the pipe is instantiated, it will include all of the attributes and the script that we added to it earlier. We’re not just creating a sprite here, but a regenerating object with a collider that can kill the player.

All you’re going to add to the same section on the PipeD script is a simple Destroy(gameObject) so it will self-destruct when it goes off the left-hand side.

If you click play now, then the game will scroll automatically and you’ll be killed if you touch either pipe. Travel far enough and those pipes will disappear and then respawn ahead of you.

But of course as the game stands, there is a big gap between the pipes and the screen looks rather empty. We could remedy this by dragging a few of the prefabs into our scene so that there is a kind of conveyor of pipes constantly coming toward us. Better though, would be to have the pipes generated in the script. This is important, as otherwise, when the character dies, the pipes at the start will have been destroyed and there will be a big blank space again.

This way, we can build the first few pipes every time the game loads up and every time the character dies, to reset everything back to normal.

Infinite challenge

Now you’re going to create a public pipe_up and a public pipe_down in your Character script. This way, you can reference the objects you’ve created by dragging the prefabs onto the character object, just like when you added pipe_down to your Pipe script.

You’ll need to add this:

    public GameObject pipe_up;
    public GameObject pipe_down;

Then we’re going to create the following method:

    public void BuildLevel()
    {
        Instantiate(pipe_down, new Vector3(14, 12), transform.rotation);
        Instantiate(pipe_up, new Vector3(14, -11), transform.rotation);

        Instantiate(pipe_down, new Vector3(26, 14), transform.rotation);
        Instantiate(pipe_up, new Vector3(26, -10), transform.rotation);

        Instantiate(pipe_down, new Vector3(38, 10), transform.rotation);
        Instantiate(pipe_up, new Vector3(38, -14), transform.rotation);

        Instantiate(pipe_down, new Vector3(50, 16), transform.rotation);
        Instantiate(pipe_up, new Vector3(50, -8), transform.rotation);

        Instantiate(pipe_down, new Vector3(61, 11), transform.rotation);
        Instantiate(pipe_up, new Vector3(61, -13), transform.rotation);

    }

With BuildLevel(), we’ll then call this method once in the Update() method and once in the Death() method.

When the game starts, Update() is called and we place pipes in this configuration. That will make the first few challenges always identical for the player. When the player dies, the pipes will be repositioned in that same configuration too.

This layout of pipes is a good set-up for my sprite (which has its scale set to “4”) but you can play around with yours. You may also want to test the speed and distances to make adjustments to the game’s difficulty.

Go back into your scene in Unity and delete the two pipes currently there. Your “game” will just look like a blank screen and a bird. Click Play and the pipes will appear, randomizing their positions after the first few.

Closing comments

That’s pretty much the entire game! Add in some scores, maybe make it a bit more original and have the difficulty ramp up as you play. You’ll need a menu screen. It would also be a good idea to destroy the pipes on the screen when the character dies.

But once you’ve done that, you’ve got an Play Store-ready product — one very similar to an app that made another developer very rich. It just goes to show you don’t need to be a coding genius or have a big publisher behind you in order to be successful.

You just need a good idea and ten minutes!





Source link

Leave a Reply

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