How do they make browser games nowadays? Part 1

Alexander Saltykov
6 min readNov 8, 2021

--

I’m a frontend guy. I do frontend for like, 10 years already. I watched the birth of React; more than that, I remember how cool it was to discover new possibilities that jQuery offered back in 2009. And of course as any other frontend adept, I like to take my favourite tool in the journeys to unexplored fields. Grab javascript and put it in mobile apps. Grab javascript and put it on the server. Grab React and put it in game development. That being said, I’m also fond of webGL for the last 8 years:

So I spent some the whole month experimenting and reading the docs and finally got the result. If you are discoverer too, please continue reading and you will know how to use React’s declarative superpowers to create games.

Disclaimer: it won’t be an actual game, because I’m not a game designer; instead it will be more like proof of concept, interactive demo scene.

Design

We’ll create a game with top-down view where the player should kill zombies. Each time player casts the spell of fire ball, one zombie spawns on the map. The main goal is to stay alive as long as you can. There will also be gamepad support.

Step 1. Setup

Just like in any other frontend project in 2021, we have to perform an awful procedure of setting up the bundler. I hate long setups for small projects so I chose Parcel for it’s zero-code-iness (spoiler: you will have to write some config in order for copy plugin to work, we’ll talk about it later). You may choose your favourite one (or the most hype one). We will also use standard pick Three.js, the most famous webGL library and react-three-fiber, which is a glue between it and react.

First of all, create a folder and run these commands from inside it:

npm init -y
npm install parcel-bundler react react-dom three react-three-fiber
npm install -D @babel/core @babel/preset-react parcel-plugin-static-files-copy

We will also need one cool utility library for three.js — drei:

npm install @react-three/drei

Make sure you added this line in scripts section of your package.json

"start": "parcel ./index.html"

And created index.html with the following content in the root of the project.

Now, if you run npm start command, you will be able to navigate to localhost:1234 where all the action will take place.

Step 2. Basic things

Let me skip the details of react-three-fiber’s implementation and it’s concepts, there are plenty of articles and videos in the web, primarily here.

First of all, there must be the ground, or floor, otherwise everyone will be hanging somewhere in space. In the example below I created basic scene with components:

  • Canvas — root component which does all the dirty work for you: starts event loop, holds default settings and so on. Most of scene settings are provided as properties of Canvas component;
  • axesHelper — not actualy a helper because there’s no way to tell which line is for which axis. Hint: y axis oriented in top direction. It took me a lot of time to understand that. Ususally it’s z axis;
  • mesh — our floor. It also has geometry and material child nodes;
  • pointLight — light source; it’s nessesary if you want to see what’s going on in the scene.

Step 3. Camera movement

Until now default camera had static position and zoom factor. Three.js has OrbitControls helper (and drei makes it a lot easier to use) which provides you a way to control it with mouse: hold left button and move the mouse to rotate camera around certain point; hold right button to move that point and camera in XZ plane; use wheel to zoom in and out. You can also specify all sorts of settings here like constraints that prevent camera to move lower or higher certain level.

Just import component and place it anywhere in the scene as shown in this sandbox.

Step 4. Fireball

Some of you might think: “That’s too easy. It doesn’t challenge me at all”. Ok then, let’s go deeper and create fireball. If you are familiar with GLSL shaders, things will be a lot easier. I can’t write shader code so I did some research and found a demo that fits me perfect.

The idea is as simple as it seems. Sphere and cylinder form ball and flame, another cylinder looks like the steam around flaming tail. All three components have transparent shader material that is being updated in each frame, so it looks like fireball consists of burning lava.

Here you can see the complete code for this component (folder /src/fireballadded, copy it’s contents if you work locally) but let me focus your attention on some of the aspects.

File /src/fireball/fireball.jsx exports a single component which in turn consists of three others wrapped in groups. Why? Because it lets you apply transformations on all of the child elements together and maintain their positions relative to one another.

Each component has following lines:

useFrame hook is being called each time requestAnimationFrame callback fires. Depending on your hardware, it’s 60 or maybe 144 times a second.

Finally if you place Fireball component in the scene and pass scale and position properties you will get the following result:

Step 5. Characters

Next thing to do is to load player and enemy models and animate them. Where to get them from? That’s a good question. For example you can use mannequin.js or mixamo. You can even create your own one or buy it on sketchfab (and use mixamo to automatically rig it). For sake of this project I encourage you to search mixamo for suitable model and animations.

When downloading model, choose FBX binary format (.fbx) and that’s it.

27K triangles! High-poly models are no good for performance, but they do look good!

The .fbx file can be about 15 MB in size so we wouldn’t want it to be part of the bundle. Instead, let’s make them static assets and load via ajax at run time.

To do so, place them to /assets/models folder in the root of the project and add this section to package.json:

"staticFiles": {  "staticPath": "assets"}

Now the models will be copied in /dist folder automatically thanks to parcel-plugin-static-files-copy.

Note two new files, getModel.js and NPC.jsx. First one exports a hook that is responsible for asyncronous loading the .fbx file, turning shadows on (all the meshes will cast and receive shadows) while second one simply uses that hook and attaches animations to it. Pretty simple. By the way, animations that you chose in mixamo are already in .fbx file but if you want them to be shared across multiple different models, it will be better to store them somewhere.

Codesandbox has some problems with parsing the models so take a look at this cute gif. They’re doing mornings or what?

Animation clip in three.js is just a json file with keyframes and transformations for each bone in the skeleton. So it’s no big deal to import a bunch of clips, parse them and add to special object called AnimationMixer. AnimationMixer, well, mixes animations. You can play them together with different proportions. Here we’ll just use it to switch between states.

All the animations that I chose for the game

That’s it for now. Part 2, that covers such topics as gamepad support, movement and state management, will be available soon.

Update: Part 2 and Part 3 is ready to be read.

Thanks for reading the article! I hope you liked it. Here are some links to stay in touch: twitter, linkedin, github.

Resources

  1. https://codepen.io/pizza3/pen/Rwoqemx?editors=0010 — demo of the fireball
  2. https://ykob.github.io/sketch-threejs/sketch/fire_ball_2.html — another cool demo for inspiration
  3. https://docs.pmnd.rs/react-three-fiber/getting-started/introduction— react-three-fiber docs
  4. https://www.mixamo.com/ — 3D models and animations catalogue, autorigger
  5. https://boytchev.github.io/mannequin.js/ — interesting lib with characters

--

--