Tech behind WebGL browser game reDEAD

TheoTheDev
6 min readJul 9, 2022

--

The game is still in development stage and quite a lot of stuff left to be done, but I decided to make an article about the tech used in it and what main challenges were faced till this moment.

So if you are not familiar yet with the project let me briefly describe:

3D multiplayer tower defence like game, where you and 7–10 other players each day defend a city from new waves of “Orcs”.

Game is primarily done for browser and uses only web-tech, but will be ported for mobile and maybe desktop platforms (probably using cordova and electron). And below latest devlog [more like an overview] of the game [09/07/2022]

At this point after viewing the video please live your feedback in the comments, it is important for me 😉

This in an intro article and doesn’t cover features/algorithms/etc in details, but provides a general overview. If you are interested in some particular part let me know in comments and I’ll pick the most popular topic for next article.

Lets go step by step:

  1. Tech overview
  2. General architecture
  3. WebGL graphics
  4. Networking in depth
  5. Conclusion

Tech overview

The whole system uses only latest web-based tech and no game engine was used for this.

BackEnd: TypeScript / NodeJS / WebRTC / WebSockets / MongoDB

FrontEnd: TypeScript / WebGL [three.js] / WebRTC [geckos.io] / webpack

Tools and utils: VSCode, Blender, Gimp, Unity [only for assets export from store].

General architecture

So lets start from the global structure, cause it’s multiplayer it’s not only the client, but still not too complicated:

In code:

And as a schema:

Master Server is responsible for user auth / profile / leaderboards and MatchMaking.

ArenaServer is responsible for one Arena which contains 5–10 players [can actually support much more, but not needed]. And will be used in Docker container to support scaling.

WebClient just game front-end [actually can be any, web/mobile or desktop].

And the flow is following:

  1. ArenaServer registers itself in Mater and gets latest system config [units/weapons/arena params].
  2. Client loads app from the CDN and does auth in Master if needed and gets latest config.
  3. When user presses ‘Play’ WebSocket connection with Masters is established and waits Arena ip/id config params.
  4. When MatchMaking finished client gets disconnected from Master WebSocket and gets connected to ArenaServer via WebRTC container and the match begins.

WebGL graphics

As core graphics engine I use Three.js

UI & end image composer

While I was making previous games (DatTank & Wardensity) though the test devices were quite performant UI always felt quite slow and glitchy like ~15–20fps even if game render loop was at solid 60fps. And especially in cases when large blocks opacity or other effects were using css ‘transition’.

So I decided to make UI fully using WebGL. It was quite a pain to do. But it was totally worth it. Now UI feels super smooth and solid, though doesn’t have lots of HTML/CSS features and recreates only basics, for example DOM tree (div in div in div etc), fully featured text (SDF method), backgrounds, padding and so.

… BUT. Code is quite ugly and each element needs to be done manually. This can be improved, but unfortunately don’t have so much time. Screenshot attached for you to feel my pain :)

Maybe someday this will be done, but it looks like it could be a completely separate WebGL UI Lib 😅

The UI in rendered in separate RenderTarget and then merged with the rest content in a special ‘Composer’ render, which does multiple stuff like post-processing (OUTLINE/SSAO/FXAA) + UI pass + 3d UI pass + brightness & contrast.

But 3d UI is a separate topic, cause is using scene Depth to know which parts of UI need to be shown and which not.

Total composer idea is smth like this:

Landscape gfx

Terrain is split to blocks 16x16 (InstancingMesh) for the total map of size 8000x8000.

Elevation is done by using vertex mapping RGB texture (which has 4 blocks 2x2, first 3 for terrain texture mapping. Each color channel tells how much of each terrain texture to show when blending end pixel color)

Trees and other map objects just simple object clones using Instancing (not trivial InstancedMesh, but custom to have some more complicated shaders).

Water is also quite basic, with simple texture scrolling and reflections (check default three.js mirror examples).

Units gfx

At the moment each Unit is a separate SkeletonMesh with own animations.
Infantry unit has two main parts: Bottom and Top. They rotate separately and have different animations, though the model is one object.

Nothing super special, though organising proper animation blending was quite a pain cause of this splitting to two parts.
This was done to be able to run/shoot or idle/shoot or idle/throw grande etc.

Effects

As for now there’s not many visual effects yet.

  • Simple lightning system making everything ‘lighter’ near your hero unit + light map for the map (it’s also stored in terrain mapping texture).
  • Fade animation when unit dies
  • Rifle bullet / Rocket launcher missile effect
  • Rocket launcher missile explosion

Current effects are not that complicated and examples can be found in the internet, but if you have a lot of this ‘effect objects’ don’t forget to use Instancing to reduce DrawCall number which kills performance dramatically.

Networking in depth

MasterServer <-> ArenaServer [ajax]

Each arena tells master it’s health and state for stats each few seconds for stats purposes and match-making (if new empty arena registered can land players there). Done using simple ‘express.js’ & ‘cross-fetch’ node modules.

MasterServer <-> Client [WebSockets]

Used only for MatchMaking described in the first part of the article and is achieved by using plain js WebSockets.

ArenaServer <-> Client [WebRTC]

Most complex part of networking. WebRTC is designed to work for Client <-> Client connection, but in this case it’s used for Server <-> Client communication.

And all this done to get access to UDP protocol (and not TCP provided by WebSockets). This allows to get much smaller ping. But as you probably may know it’s missing reliable and ordered message delivery, so for important messages this features need to be done manually.

To make life a bit simpler I use geckos.io lib has a lot of features built in, though reliable message delivery has poor implementation (provided implementation of the lib just sends the message a fixed number of times defined by you).

A lot can be said about networking topic, but I think it should be at least one separate article.

Conclusion

Despite the development time of AzovS game is only like two month, 90% of code base is taken from previous project IconiX (http://demo.iconix.io only lobby available atm and hope to finish after releasing AzovS). And at first 80% of IconiX code was taken from DatTank game (it was released and was available for a year or so and had 250k players in total and 10k per day max).

And the total journey of my web game development is like 8 years already.

Here’s a flash-back of Wardensity RTS game which was never finished, but was a good start 😎

Follow me to get more updates:
Twitter: https://twitter.com/theo_the_dev
Discord channel: https://discord.gg/GbYNdmTg
Youtube: https://www.youtube.com/channel/UCM5RmcaPiHEcVZ02VrgH9OA

If you like the topic leave a comment about what part of the game implementation is the most interesting for you! Cheers!

--

--

TheoTheDev

Founder of indie studio ‘NextWebGames’ & solo developer of Azovs and IconiX games