Introducing React Game Kit

September 15, 2016

See Ken’s slides from his React Next talk.

Introducing React Game Kit

React Game Kit is Formidable’s newest release, written by the one and only Ken Wheeler. Since Ken is busy killing it in Israel at ReactNext, I’m giving a rundown of what React Game Kit is and why you should use it. Ken’s slides from his React Next talk can be found here, and I highly recommend you take a look at them.

Game Dev Basics

Should Can you build a game with React?

You sure can!

Why would you build a game with React?

  • The same game code can work on the web, iOS & Android
  • You primarily write React code
  • You dont feel like learning Unity
  • You can hot reload game logic

What is a game?

“A form of play or sport, especially a competitive one played according to rules and decided by skill, strength, or luck.”

Today we are going to learn how to make a 2d platformer game with ReactJS.

Basic Concepts

Game Loop

A programmatic loop that gets input, updates game state and draws the game.

Tick

Each step of the loop.

Update Function

A function called on each tick where game logic is checked.

Stage

The main game container to which game entities are added.

Sprite

An often animated bitmap graphic derived from a larger tiled image of states and steps.

TileMap

A large graphic created by rendering a matrix of position indexes derived from a smaller set of common tiles.

Physics Engine

A class that simulates physical systems.

Rigid Body Physics Engine

A physics engine that assumes that physical bodies are not elastic or fluid.

Physics World

A class that provides a set of conditions that the simulation abides by.

Physics Body

A class that acts as an entity inside the physics world.

This sounds hard.
But it doesn’t have to be!

Introducing: react-game-kit. A collection of ReactJS components and utilities that help you make awesome games. It’s pretty fun, all of the slides here are built with it. Oh, and it works on React Native too!

The Loop

How does the loop work?

requestAnimationFrame

let animationFrame; const loop = () => { // Update logic animationFrame = requestAnimationFrame(loop); } animationFrame = requestAnimationFrame(loop);

How can I implement this in React with react-game-kit?

class Game extends Component { render() { return ( // Child components get this.context.loop ) } }

Wait, how does context work?

class Example extends Component { static contextTypes = { loop: PropTypes.object, }; loop = () => { //Do stuff here }; componentDidMount() { this.loopID = this.context.loop.subscribe(this.loop); } componentWillUnmount() { this.context.loop.unsubscribe(this.loopID); } }

Scaling

How can we size and scale our game?

transform: scale()
Rounding errors on subpixel floats mean we have to manually round & scale. react-game-kit provides a Stage component to help with this.

class Game extends Component { render() { return ( // Child components get this.context.scale ) } }

Most screens you are targeting will have a 16:9 aspect ratio.

getWrapperStyles() { const x = Math.round(this.state.x * this.context.scale); return { position: 'absolute', transform: `translate(${x}px, 0px) translateZ(0)`, transformOrigin: 'top left', }; }

That’s cool, but won’t my images be blurry?

Not if you pixelate them!

getImageStyles() { const scaledWidth = Math.round(this.props.width * this.context.scale); return { width: scaledWidth, imageRendering: 'pixelated' }; }

Sprites

So, how do sprites work?

Create a sprite map.

character-sprite
character-sprite-grid
class Sprite extends Component { render() { return (![]({this.props.src})) } }
getImageStyles() { const left = this.state.step * tileWidth; const top = this.state.state * tileHeight; return { position: 'absolute', transform: `translate(-${left}px, -${top}px)`, }; }

react-game-kit provides a Sprite component to simplify this process.

return ( );

Tilemaps

What in the world is a tilemap?

Tile maps use a tile atlas to use a few graphics to create a “level”.

Tile maps have tiles and layers.

boardwalk tile
buildings

Ok, so what does the map look like?

const tileMap = { rows: 4, columns: 8, layers: [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, ], ], };

Parsing a tile map

layers.forEach((l, index) => { const layer = []; for (let r = 0; r < rows; r++) { // Loop over rows for (let c = 0; c < columns; c++) { // Loop over columns const gridIndex = (r * columns) + c; // Get index in grid if (layer[gridIndex] !== 0) { // If it isn't 0 layer.push({ row: r, column: c, tileIndex: layer[gridIndex] }) } } } }

react-game-kit provides a TileMap component to simplify this process.

getTileStyles(column, row, size) { const left = column * size; const top = row * size; return { height: size, width: size, overflow: 'hidden', position: 'absolute', transform: `translate(${left}px, ${top}px)`, }; }

Why not just make it one big image?

if (tile.index === 2) { return ![]({src}); } return ![]({src}); )} layers={[[ 0, 0, 2, 0, 2, 0, 1, 1,]] }

Physics Engine

You probably don’t need a physics engine.

// Update loop const update = () => { if (rightKeyPressed) { character.x += 1; } if (leftKeyPressed) { character.x -= 1; } };

But let’s say you do want physics. react-game-kit provides physics helpers provided by matter-js.

class Game extends Component { render() { return ( // Children get this.context.engine ) } }
class WorldExample extends Component { physicsInit = (engine) => { const ground = Matter.Bodies.rectangle( 512, 448, 1024, 64, { isStatic: true }); Matter.World.addBody(engine.world, ground); } render() { return ; } }

When using matter-js physics, it’s important to do physics updates after the world has updated.

class WorldChild extends Component { update = () => { //Logic goes here } componentDidMount() { Matter.Events.on(this.context.engine, 'afterUpdate', this.update); } componentWillUnmount() { Matter.Events.off(this.context.engine, 'afterUpdate', this.update); } }

Using physics bodies

class WorldChild extends Component { //... render() { return ( { this.body = b; }} > ); } }
class WorldChild extends Component { move = (body, x) => { Matter.Body.setVelocity(body, { x, y: 0 }); }; update = () => { const { body } = this.body; if (keys.isDown(keys.LEFT)) { this.move(body, -5); } else if (keys.isDown(keys.RIGHT)) { this.move(body, 5); } }; //... }

Performant use of physics data for positioning

mobx

import { observable } from 'mobx'; class GameStore { @observable characterPosition = { x: 0, y: 0 }; } export default new GameStore();
import { observer } from 'mobx-react'; @observer class WorldChild extends Component { move = (body, x) => { Matter.Body.setVelocity(body, { x, y: 0 }); }; update = () => { const { body } = this.body; if (keys.isDown(keys.LEFT)) { this.move(body, -5); } else if (keys.isDown(keys.RIGHT)) { this.move(body, 5); } store.characterPosition = body.position; }; //... }
import { observer } from 'mobx-react'; @observer class Enemy extends Component { getStyle() { const {x, y} = store.characterPosition; return { position: 'absolute', transform: `translate(-${x}px, -${y}px)` } } render() { return } }

Now go forth and build some dope games in React, and check out the source code for react-game-kit.

Related Posts

Flexible Charting in React with Victory (and Introducing FormidableCharts)

November 9, 2016
Victory: React charts tailored to your data Charting directly with d3 can be difficult, but other libraries are often too simplistic. Enter Victory: React charting that is easier than direct use of d3.js but with as much flexibility as possible. Victory allows fully customized charts ranging from...

Victory 0.12.0: The One True Tooltip

September 19, 2016
We are pleased to announce that the latest release of Victory finally includes tooltips! This feature has been in demand for a while, but nailing down an implementation strategy proved challenging. No other chart element exhibits the range of behaviors or visual possibilities as the simple tooltip....

Announcing Victory 0.10.2 and VictoryNative

August 5, 2016
It’s been a while since the release of Victory 0.9.0 back in June, so we’re excited to add several new features and bug fixes in order to continue making the creation of D3 charts in React as painless as possible. This post will explore some of the highlights of the new...