Building a Game with Three.js, React and WebGL

I’m making a game titled “Charisma The Chameleon.” It’s built with Three.js, React and WebGL. This is an introduction to how these technologies work together using react-three-renderer (abbreviated R3R).

Check out A Beginner’s Guide to WebGL and Getting Started with React and JSX here on SitePoint for introductions to React and WebGL. This article and the accompanying code use ES6 Syntax.

Hands holding a red pill and a blue pill, with a chameleon on one arm.

How It All Began

Some time ago, Pete Hunt made a joke about building a game using React in the #reactjs IRC channel:

I bet we could make a first person shooter with React!
Enemy has <Head /> <Body> <Legs> etc.

I laughed. He laughed. Everyone had a great time. “Who on earth would do that?” I wondered.

Years later, that’s exactly what I’m doing.

Gameplay GIF of Charisma The Chameleon

Charisma The Chameleon is a game where you collect power-ups that make you shrink to solve an infinite fractal maze. I’ve been a React developer for a few years, and I was curious if there was a way to drive Three.js using React. That’s when R3R caught my eye.

Why React?

I know what you’re thinking: why? Humor me for a moment. Here’s some reasons to consider using React to drive your 3D scene:

  • “Declarative” views let you cleanly separate your scene rendering from your game logic.
  • Design easy to reason about components, like <Player />, <Wall />, <Level />, etc.
  • “Hot” (live) reloading of game assets. Change textures and models and see them update live in your scene!
  • Inspect and debug your 3D scene as markup with native browser tools, like the Chrome inspector.
  • Manage game assets in a dependency graph using Webpack, eg <Texture src={ require('../assets/image.png') } />

Let’s set up a scene to get an understanding of how this all works.

[affiliate-section title=”Recommended Courses”][affiliate-card title=”The Best Way to Learn React for Beginners” affiliatename=”Wes Bos” text=”A step-by-step training course to get you building real world React.js + Firebase apps and website components in a couple of afternoons. Use coupon code ‘SITEPOINT’ at checkout to get 25% off.” url=”https://ReactForBeginners.com/friend/SITEPOINT” imageurl=”https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2017/07/1501203893wesbos.jpg”][/affiliate-section]

React and WebGL

I’ve created a sample GitHub repository to accompany this article. Clone the repository and follow the instructions in the README to run the code and follow along. It stars SitePointy the 3D Robot!

SitePointy the 3D robot screenshot

Warning: R3R is still in beta. Its API is volatile and may change in the future. It only handles a subset of Three.js at the moment. I’ve found it complete enough to build a full game, but your mileage may vary.

Organizing view code

The main benefit of using React to drive WebGL is our view code is decoupled from our game logic. That means our rendered entities are small components that are easy to reason about.

R3R exposes a declarative API that wraps Three.js. For example, we can write:

<scene>
  <perspectiveCamera
    position={ new THREE.Vector3( 1, 1, 1 )
  />
</scene>

Now we have an empty 3D scene with a camera. Adding a mesh to the scene is as simple as including a <mesh /> component, and giving it <geometry /> and a <material />.

<scene>
  …
  <mesh>
    <boxGeometry
      width={ 1 }
      height={ 1 }
      depth={ 1 }
    />
    <meshBasicMaterial
      color={ 0x00ff00 }
    />
</mesh>

Under the hood, this creates a THREE.Scene and automatically adds a mesh with THREE.BoxGeometry. R3R handles diffing the old scene with any changes. If you add a new mesh to the scene, the original mesh won’t be recreated. Just as with vanilla React and the DOM, the 3D scene is only updated with the differences.

Because we’re working in React, we can separate game entities into component files. The Robot.js file in the example repository demonstrates how to represent the main character with pure React view code. It’s a “stateless functional” component, meaning it doesn’t hold any local state:

const Robot = ({ position, rotation }) => <group
  position={ position }
  rotation={ rotation }
>
  <mesh rotation={ localRotation }>
    <geometryResource
      resourceId="robotGeometry"
    />
    <materialResource
      resourceId="robotTexture"
    />
  </mesh>
</group>;

And now we include the <Robot /> in our 3D scene!

<scene>
  …
  <mesh>…</mesh>
  <Robot
    position={…}
    rotation={…}
  />
</scene>

You can see more examples of the API on the R3R GitHub repository, or view the complete example setup in the accompanying project.

Continue reading %Building a Game with Three.js, React and WebGL%


Source: Sitepoint