My Journey with React Native Game Engine Part I: Starting the Project

William Yang
9 min readSep 26, 2018

--

A quick background on me — I’m a beginner programmer who is currently attending a coding bootcamp, and for one of our project weeks, we were to do a project in React Native. One of my instructors suggested looking into React Native Game Engine (RNGE) as a challenge to me, and when I looked into it, I thought, “Hey, this seems like fun!” As I progressed through learning a new engine, I wanted to share my struggles and discoveries with React Native Game Engine, so I could help other beginner programmers like me with working with the engine.

Hacking Things Together

Earlier, I said that RNGE seems like fun, but my initial thought was, “Well, where do I get started?” There are a couple really good start up projects that let me get my feet wet:

RNGE Physics Demo (Left) RNGE DK Demo (Middle) RNGE Single Touch Demo (Right) from React Native Game Engine Handbook

Both had simple instructions on the GitHub for both iOS and Android — simply clone the repository to your local machine, open a terminal/command line at the folder, run npm install, cd into the folder, and run react-native run-ios or react-native run-android.

I focused on the Single Touch and Physics part of the Handbook because those were the features I wanted for my game. After a lot of copying and pasting and hacking together some parts I didn’t understand from the Handbook and Donkey Kong, I managed to produce an app that had some things wired up. I won’t get too much into how I did this monstrosity.

First attempt at putting things together…poor cat!

From The Beginning: Learn the Game Engine

I decided to scrap my first work of ‘art’, and wanted to start from minimal pieces to learn how each part worked. My goal for this first part was to get a floor to render on the screen. Before we get started, I want readers to know I will just be doing iOS for this tutorial, and I will have the Gist at the end of the article.

Make sure you already have the following set up:

Starting a new React Native App

To get started, go to a new directory and use create-react-native-app <folder-name>. After it finishes downloading everything, go into the folder with cd <folder-name> . Run the following commands to install the react-native-game-engine and matter-js libraries:

yarn add react-native-game-engine matter-js

Once they finish downloading, use yarn run ios to start your development server. If you don’t have yarn , npm will work as well

UPDATE: create-react-native-app does not work anymore. To start your new app, you will have to install the new expo-cli. Once your folder is setup, install react-native-game-engine and matter-js .

$ npm install -g expo-cli
$ expo init my-app
$ cd my-app/
$ expo start

This will bring up the new Expo Dev Tools — a browser GUI that helps you choose to run your Simulator, has a console, and can help publish your app as well. Press Run on iOS Simulator to start your Simulator.

You should see a screen like this in your Simulator.

Adding the GameEngine to the Project

In your App.js file, add these two import statements at the top of your file:

import Matter from "matter-js";
import { GameEngine } from "react-native-game-engine";

Matter.js is a Javascript Physics Engine library that helps you create shapes, add collision detection, and gravity to your project.

React Native Game Engine is a React Native library that provides a Game Loop that will constantly update your state to reflect changes to the entities you render onto the screen. More on this later.

Now that we have the React Native Game Engine imported, let’s add in the GameEngine component into our render() method.

export default class App extends React.Component {
render() {
return (
<GameEngine style={styles.container}>
<StatusBar hidden={true} />
</GameEngine>
);
}
}

<StatusBar hidden={true}/> will hide the iOS status bar when loading the project. You will have to add StatusBar to your 'react-native' imports. We can also remove Text and View for the time-being.

import { StyleSheet, StatusBar } from 'react-native';

In the const styles, remove the alignItems and justifyContent. We won’t be needing those.

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});

Now that we have our GameEngine added, we now need to add Systems and Entities to our engine. Let’s start with a simple entity.

Getting a Box to Render on the Screen

First, let’s get a simple Box to appear on the screen. Create a new file called Box.js.

Box.js will render a View that has styling on it to make it look like a box. Box.js will also take in a few props: size, body, and color. Box.js will take those props and use them to help style itself depending on what values are passed into the Box component.

Let’s return to our App.js to render the Box through our Game Engine. Before we add it to the Game Engine, let’s make a few const variables to make rendering our Box a bit easier. If you have import issues, I will talk about the import statements a bit further down.

const { width, height } = Dimensions.get("screen");const boxSize = Math.trunc(Math.max(width, height) * 0.075);const initialBox = Matter.Bodies.rectangle(width / 2, height / 2, boxSize, boxSize);

Here, we are setting the width and height to the Dimensions of our screen. This way if we are playing on different iPhone screens, it will have the same ratios. We also set boxSize to be a certain ratio of the width and height. Last, we create an initialBox that is a Matter.Bodies.rectangle with an x position of half of the screen width, a y position of half of the height position, and a width and length of our boxSize.

Matter.Bodies.rectangle takes in an x, y, width, and height. Under the hood, matter-js will take in this information and create a rectangular shape for us to use with our engine and world later.

We will need to add Box.js to our import statements, and we will need to update our react-native import statement with Dimensions:

import Box from './Box';
import { Dimensions, StyleSheet, StatusBar } from 'react-native';

In our GameEngine component, add a new initialBox object to our entities prop like below. Our GameEngine will take in the object properties and use them to help render the Box.

<GameEngine
style={styles.container}
entities={{ initialBox: {
body: initialBox,
size: [boxSize, boxSize],
color: 'red',
renderer: Box
}}>

<StatusBar hidden={true} />
</GameEngine>

With these changes, we will see a red box on our simulator.

A simple red box

Congrats! We finally have something on the page. Now let’s get a floor rendered.

Getting a Floor to Render

Let’s make another const above our App class.

const floor = Matter.Bodies.rectangle(width / 2, height - boxSize / 2, width, boxSize, { isStatic: true });

Once again, we make a rectangle, but this time we’re placing it in the bottom middle of the screen, and making it as wide as the screen, and as tall as a box. We’re also giving it an option of isStatic, which will determine whether the object moves or not.

Now let’s add our new floor to the list of entities in our GameEngine component.

<GameEngine
style={styles.container}
entities={{ initialBox: {
body: initialBox,
size: [boxSize, boxSize],
color: 'red',
renderer: Box
},
floor: {
body: floor,
size: [width, boxSize],
color: "green",
renderer: Box
},
}>
<StatusBar hidden={true} />
</GameEngine>

Once this has been added, refresh your simulator and now you should see a green box at the bottom in the simulator as well.

But, nothing is really happening! Why isn’t our box moving?

Adding Gravity

Here is where matter-js comes into play. We’re going to build an engine and a world for our simulator. With our other consts above our App class, add these two lines:

const engine = Matter.Engine.create({ enableSleeping: false });
const world = engine.world;

Matter.Engine.create creates a new Physics engine, a controller that manages updating the simulation of the world. Engine.world will create a world that will contain all simulated bodies and constraints.

Now that we have a world, let’s add our box and floor to the world. Matter.World.add takes in two parameters: a world, and an array of Matter.Bodies. We can add the line below after all our consts.

Matter.World.add(world, [initialBox, floor]);

Now, let’s add our engine and world to our list of entities in the GameEngine component.

entities={{ 
physics: {
engine: engine,
world: world
},
initialBox: {
body: initialBox,
size: [boxSize, boxSize],
color: 'red',
renderer: Box
},
floor: {
body: floor,
size: [width, boxSize],
color: "green",
renderer: Box
}
}}

Now if you refresh the project, nothing happens! What gives?

We have to add Systems to our GameEngine that will update our engine and game state, otherwise nothing will move. Systems will be an array of functions, that will be called on every game tick. Meaning, these functions will be run at a very fast speed, and can be used to check and update our game.

In our App.js, let’s add a Physics function above our App class.

const Physics = (entities, { time }) => {
let engine = entities["physics"].engine;
Matter.Engine.update(engine, time.delta);
return entities;
};

Physics takes in an object of entities, and an Timer object of time. It will set a local variable engine to the entities object property physics.engine, and it will update it for every delta change in Time. Lastly it will return the updated entities.

For those wondering where does time come from — Systems passes in entities and a set of arguments to each function as parameters. These arguments are:

  • Touches
  • Screen
  • Events
  • Dispatch
  • Time

Time has extra properties: current, previous, delta (currentTime — previousTime), and prevDelta.

Now we can add Systems to our GameEngine.

<GameEngine
style={styles.container}
systems={[Physics]}
entities={{
physics: {
engine: engine,
world: world
},
initialBox: {
body: initialBox,
size: [boxSize, boxSize],
color: 'red',
renderer: Box
},
floor: {
body: floor,
size: [width, boxSize],
color: "green",
renderer: Box
}
}}>
<StatusBar hidden={true} />
</GameEngine>

Refresh your simulator, and we will see a red box falling down to our green

Houston, we have gravity

Congrats! We now have a box that is affected by gravity and has collision!

Part I: Conclusion

That will be the end of Part 1 — we learned a lot in this part:

  • How to add Game Engine to our React Native project
  • How to use Matter-js to create and render entities to our screen
  • How to use Matter-js to add physics to our world

In Part 2, I will go over how to add Touches and Bouncing to our game.

I hope everyone enjoyed reading this article, please leave any comments or questions you have.

Here is the gist for App.js

Links and Resources

Once again, big thanks bberak and liabru for these awesome libraries! Wouldn’t be able to work with something awesome without these guys.

--

--