Creating A 2D Game

Goal

In this tutorial we want to show you how easy it is to create a 2D cross-platform mobile game with React Native, Expo, and Three.js. We'll be recreating a version of the popular (and addictive) game Flappy Bird!

Because this game uses Three.js you will only be able to run it on a physical device! No Simulators 😐

Preloading

We want to load all of our assets before we start the game. All of the code necessary to do this is already included in the 2D Game template as it is commonly used across projects.

Building our game

The first thing we want to do in our game is define some global constant settings that can be edited to manipulate the look and feel of the experience. A good place for this would be right outside the Game class

GROUND_HEIGHT: Amount of ground that we will see at the bottom of the screen

Feel free to play with these values to create a unique experience!

Now that we have some of the laborious stuff out of the way, let's add something to our scene!

The first thing we should add is an awesome background! The background should be a static sprite that fills in the entire screen. To do this, open Game.js in the root directory, add a function called setupBackground, then call this function in the onSetup function.

onSetup =async({ scene })=>{// Give us global reference to the scenethis.scene = scene;awaitthis.setupBackground();};
setupBackground =async()=>{// We will be doing some async stuff in here :}};

Because we will be building a lot of static nodes we should create a helper function for that.

Tip Sprites are used for images and animations. We use nodes for positioning and managing state.

Lets create the players display size. If you look at our player sprite in assets/sprites/bird.png you will notice that there are three birds on it! When we make an animation in a video game we load in a sprite sheet, which is an optimal image containing all of the frames of an animation. Our display size is the image size but the width is divided by the number of birds, so 108 / 3 = 36 :)

Make a Sprite just like before but this time we will add a few more properties for animating.

tilesHoriz: (Tiles Horizontal) is how many tiles we have across (in our case 3).

tilesDispDuration: How long each tile is on screen for before it goes to the next one, this is measured in milliseconds.

size: this is the size we defined earlier.

Finally make a Node, give it our animated Sprite, and add it to the scene!

If we were to run the app right now we would see the bird in the middle of the screen. But Wait! It's not animating, this is because we need to update it each frame. To do this we need to add some code to our updateGame function.

Every few seconds (SPAWN_RATE) the pipes will spawn! However if you run the app you still won't see the pipes on screen 😱😭 This is because we aren't moving them yet! Let's move them all to the left and when they move past the player we should increment the score!

If the game has started then we want to add gravity * delta to our velocity

Here we set the birds rotation (in radians). Notice how we clamp it with min/max. This way when the bird has upwards velocity it spins to point up, and the opposite happens when it's falling down

Let's add another instance of updating the bird's flapping animation when we are playing the game

Apply velocity to our bird's position

And that's all we need to give our user a way to control the bird, pretty easy! 😁

Collisions

Right now our bird doesn't have much conflict in it's life. It just flaps away with no consequences, that is until now of course! We need a way to end the game when our bird hits a pipe or get's tired and falls on the ground we haven't built ;)

Notice that we build two copies of the ground. Once one floor goes off screen we place it to the back and that creates our floor loop!

Set the groundNode group's position to be at the bottom of the scene

Save a reference to the top of the ground for collision purposes. Then move the ground slightly forward on the z-axis so that it appears in front of the pipes.

Ok so now we have a ground showing up but it doesn't move with the player 😵😨 Because the ground moves infinitely we need a function that not only moves the ground but also checks if it's off-screen so that it can reset it.

define the components state and give it a property score then assign score to 0

Let's build a helpful function to increment the score by 1 whenever it's called

Here we will define what the score label will look like. We use a native Text component to do this! 🤤

Now we will add our score component to the main render method 😬

Loading Sounds

Nothing makes a game for more real than good sound effects. Lucky for us everyone at Expo is a huge audiophile and as a result of this we have a dope audio APILet's add sounds whenever a key moment occurs: