Introducing React Game Kit

avatar
Emma Brillhart
15 Sept 2016
  • Share

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);
js

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

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

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);
  }
}
js

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 
    ) 
  }
}
js

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', 
  }; 
} 
js

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' }; 
} 
js

Sprites

So, how do sprites work?

Create a sprite map.

character-sprite

character-sprite-grid

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

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

return ( );
js

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, ], 
    ], 
};
js

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] }) 
      } 
    } 
  } 
}
js

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)`, 
  }; 
}
js

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,]]
}
js

Physics Engine

You probably don’t need a physics engine.

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

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 
    ) 
  }
}
js
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 ; }
} 
js

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);
  }
} 
js

Using physics bodies

class WorldChild extends Component { 
  //... 
  render() { 
    return ( { this.body = b; }} > ); } } 
js
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); 
    } 
  };
  //...
}
js

Performant use of physics data for positioning

mobx

import { observable } from 'mobx';
class GameStore { 
  @observable characterPosition = { x: 0, y: 0 }; 
} 

export default new GameStore(); 
js
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; 
  }; 
  //...
 } 
js
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 } 
} 
js

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

Insight, imagination and expertly engineered solutions to accelerate and sustain progress.