Oh boy, this one's a complicated one. It begins with a project called "The Game" or "Project X" judging by an old directory name; it never had a proper name. Like any good forever-project, it has many stages. You might have seen some already, like Amar Engine (itself a combination of many projects), and Metaplains before that. The saga continues.
It's likely that I wasn't using version control when I started this (probably around 2012) and then in 2014 I decided to create a repo called x-legacy where I made every iteration of this project into a commit, so I can go back in history, and I'm glad I did. There were 22 versions! This is what I see when I run the latest version:
Looks a bit familiar, no? This is basically the One Game that I've been trying to get out of my system for over a decade.
The game wouldn't run properly for reasons we'll explore later, so rather than hack away at this to make it work, I decided to go for the newer version of this in a second repo called x-proto, which I would have considered the same project. The separation is purely because before a certain iteration I wasn't using git.
I had almost forgotten that I ported Amar Engine to the web. I only remembered because as I was writing up the details of Amar Engine, I thought "I could have sworn I did something with butterflies". This led me to search my GitHub for "butterfly" and find these two repos! Then I recorded a short video to showcase x-proto:
Instead of Java, Three.js was just picking up some steam at the time so I decided to give it a go. This was already a big step for me as I likely started off by writing directly on top of WebGL until I realised that I'd just be doing the same thing again and again for years if I didn't start using libraries. I still didn't concede entirely it seems; I implemented some basic data structures from scratch for some reason, like linked lists! I did find some interesting tidbits here and there as I was exploring the codebase however.
For physics, I used a library called Physijs which is based on a library called ammo.js. Today, ammo.js would used WebAssembly, but this was in the days of asm.js and Emscripten which ammo.js used; WebAssembly wasn't a thing yet.
I also wrote my own player controller apparently. There's one built in to Bullet Physics (which I used in Amar Engine eventually) but not here. I glued a capsule collider on top of a sphere, and presto! The sphere doubled as a ground collider, which told me when the player was standing on something (and could thus jump) as well as when I should play walking sounds.
To this I also added some logic to allow the player to both interact with, as well as pick, entities. The difference was that interactions are relatively course, while picking is fine. Interacting was done via sending a message to entities that touch a slightly larger cylinder that envelopes the player, while picking is done via casting a ray from the player's camera in the look direction.
When the player chops down trees, he's interacting with them (and can only do so while holding an axe). I added a picking handler in butterflies, but it doesn't actually do anything except log the entity. I suspect that my intention was to make it possible to catch these butterflies with a net.
I also created a custom camera controller. By scrolling, you can transition it smoothly between a normal first-person camera, and a third-person camera that orbits you. I remember being especially impressed by the camera controls in Super Mario 64 and wanted to build a camera controller like that. These days there's probably a Unity add-on that does all that and more!
I also attached a directional sun light to the player (with the direction changing depending on the time of day) such that only shadows near the player get rendered, as rendering all shadows was quite slow. This is why you only see shadows for the trees near you, though I could have made the transition smoother and done other tricks to make things seems smoother, like fog.
I remember thinking that it would be silly to build a UI system from scratch (progress!) as that's literally the one thing browsers are great at. So all UI was using HTML, including a draggable inventory window with draggable slots (using the Drag and Drop API). The inventory window itself was also draggable.
The chat UI was very clearly inspired by Minecraft. I'm pretty sure I reused this later while writing mods for Manyland.
Now here's something new! Instead of a skybox, the colours in the sky here were procedural, and rendered with shaders. You can see the shaders for yourself, but essentially I created some smooth transitions between gradients depending on the time of day. I really wanted to create some pretty sunrise/sunset colours as the sun was at the horizon.
The sun and the moon were just quads; I didn't bother to render them in the shader, nor did it really seem to make sense to do it that way. The stars were also (small) quads. I remember trying to render the stars in shaders, but it was too expensive and didn't even look that good. Today, I think I could do it in shaders more efficiently and better-looking than as geometry.
For terrain, I had ported over the older shaders, passed some textures, and blended these based on terrain height. The terrain was much much bigger this time around though.
I remember having a big vision for this, so you can actually go to the stars. I think I looked at Kerbal Space Program, watching their skies, and wanted to do something similar.
I tried to make a universal format for scenes, one JS file with a bunch of definitions (name, models, entities, player controller and starting position, then an init function where you can do anything you like). The idea was to make this as extensible as possible, so people could create their own scenes and this game would be a way to "browse" them. In other words, the beginnings of a metaverse interoperability standard.
I always had it in my mind that this would be a big, decentralised place, where all assets and logic are loaded off of something like IPFS or literally just URLs that can be discovered. I eventually did my PhD on this topic, but that's another story.
Coming from Java and the early days of Minecraft mods, I remember how tough it was to:
As a result, this inspired me to treat moddability as a core tenet as opposed to an afterthought, while still keeping the codebase relatively well structured and borrowing the stronger OOP concepts from Java.
It looks like libfabric was already starting to see some use here, though I had to downgrade to a much much older version of the server to get it to work with the version of the client I was using. It looks like I didn't want to build this from scratch right away, and actually used an early version of PeerJS but quickly ran into some limitations with what I wanted to do (sparsely connected topologies). I got a chance to explore this further through my PhD of course!
I also had a fun feature where all text chat would be converted into speech. I think I saw this feature in a moon landing simulator-type game (I forget the name) and there were Let's Plays of people having a blast with it.
At the time, I think browsers didn't have built-in text-to-speech (or at least it wasn't very well supported) and I was using a client-side library called meSpeak.js. I actually preferred the much more mechanical-sounding, Microsoft Sam-esque voices.
Browsing through the codebase, I noticed a lot of other interesting things.
requestAnimationFrameto something like one tick per second.
Countdownthat would take a callback, and allow you to decrement a counter in your async code. Once the counter reaches zero, the callback fires. This satisfied my craving for some semblance of clean code.
/Escape Pod/Games. I can think of many reasons why they stopped doing this, but today I might use GitHub Pages for this.
fetchwasn't a thing back then, so I had written some utility functions around XHR to make it a bit easier to fetch resources.
GAME.namespace('entities.flora').Tree = ....
prototype. JS has come a long way since then!
exec'd. The intention behind this is described above, and I remember thinking "this is not very safe" but I didn't want to create restrictions on what could or couldn't be done in a Scene, so figured this could be remedied through some human scrutineering system instead.
It was not at all straightforward to get this game to run. The thing about web apps is that standards and browsers change gradually over time, and it's not as easy to just install an old runtime alongside the modern one to run an old project, like installing Python 2.7 or Java 8. Browsers had changed a LOT in that time, so I had to do a couple of things.
[object Object]) and set that string as an attribute on the HTML tag. So I tweaked that slightly so that the inventory is temporarily stored on the game instead so I can hold my axe.
saveData.seed(with a fallback) and
saveDatanever existed. It seems like browsers were much more forgiving back then (before Optional Chaining), such that the whole thing would just be undefined rather than throw an error.