I posted a bunch of short devlogs between 2012 and 2014. They’re collected here in one place.

2012-11-19: Encounter: a devlog

I’m working on a WebGL game using the three.js library.

The project is to recreate ENCOUNTER, the frenetic C64 game ENCOUNTER by Paul Woakes.

This is the first update as a video.

2012-11-19: begone, intersections

Today I fixed the player shots passing through the obelisks like they’re not there.

After a couple of hours of head-scratching last night with a pen and paper I remembered I had coded all this before, a long time ago - specifically in 1999, for a university project. So I dug out the ancient Java code and converted it to Javascript! Thanks, past me.

You can play this version right now - and see the source code, if you’re so inclined - here on github.

2013-01-27: a nasty problem cracked

Hurray! After a longish hiatus I’m back with some time to spend on Encounter.

The code I’d rescued from the 90s got me half-way there - by moving circles apart that were intersecting - but didn’t really take care of collisions. If a shot hits an obelisk, it must bounce off at the correct angle. Sounds easy right?

In theory there are some vector formulae you can use to figure out this kind of reflection. That was surprisingly straightforward (or bug-free at least) - I just implemented the formulae with the Vector3 class.

The really tough part is making it all work with the coordinate and rotation system used by three.js. For example, in three.js zero degrees ‘north’ points down the Z axis and not up the X or Y axes. And you absolutely have to get used to thinking in radians. It’s incredibly easy to get confused.

radian compass

My killer bug turned out to be trigonometry. I was struggling with acos/asin to find my angles of collision, which don’t work in 100% of cases. Some hopeful googling pointed me to atan2, which works for all quadrants. A little bit of thinking and rework and all my tests went green.

I could never have fixed this bug without the use of Jasmine. When your brain really starts to hurt, write out some unit tests for all the cases and get them all green. Don’t leave home without Jasmine!

So: the collisions are looking pretty good now. You can bounce your shots intimidatingly back at yourself. There are a couple of cases where a glancing blow doesn’t give the right bounce, but that’s a different problem I already know about. Something for next time : )

2013-02-02: GUIs and controls and everything

Whenever I’ve worked on game or art projects in the past it’s always been frustrating getting a live view of what’s happening in the engine. Tools like a debugger to watch variables in real time, or a decent GUI for in-game objects were always a luxury and time-consuming to write.

So I am absolutely delighted that dat.gui exists. It makes it incredibly easy to expose variables and mess with them in real time. I love it:

dat.gui example

That literally took me 30mins. Amazing.

2013-11-14: back to work

I got back into Encounter because I wanted to stress-test the engine. A big playfield with all these obelisks, and we’re only firing a few shots at a time. Surely we can do something more spectacular…

What if I put a shooter in the playfield spewing shots in all directions, bouncing willy-nilly? Would three.js hold up? (hint: yes) Would my frankly underpowered Macbook Pro melt under the pressure? (hint: yes again)

I spent an hour or two adding a new ShotSpawner object that would rotate in place, firing a shot every 100ms or so. The resulting noise is intolerable : )

With this running and bouncing spheres all over the playfield, after a few minutes my Macbook screen started to pulse off into dead blackness as the temperature reached critical levels. After some quick thinking a swift Cmd-W to the Chrome window killed off the 3D-ness and gave the CPU a break. Then off to install a temperature monitor. No permanent damage.

Today’s work was all refactoring - putting the Obelisk and Grid code into dedicated classes and labelling constants.

For me, trying to do ‘normal’ OO things in Javascript when I’m not using the language every day is an absolute misery. There’s no single way to do things, so the resources you look up are not consistent. I’m not deep enough into the Object/prototype semantics regularly for it to stick in my brain.

javascript: the evil parts

I’m enjoying inching it forward. Also, it occurred to me this morning that with some WebSockets this could be a multiplayer game. Dangerous ambitions at the pace it’s going.

2013-12-07: crash bang wallop what a picture

1. dat.gui has bugs and nobody cares

A large chunk of today was taken up discovering and understanding a bug in dat.gui. If you feed it a variable to track - say your current X axis rotation - it’s impossible to guarantee what precision comes out on the display. So my rotation float - which only varies between zero and Pi - gets clamped to an integer, making it useless.

Turns out this is broken in the library, as reported on Google Code. Worryingly the dat.gui project seems to be abandoned. I added the patch to the issue (as figured out by someone else). Until the committers wake up again (fingers crossed) I’ll be working on a manually patched build.

2. Refactoring, always refactoring

There’s always a ton of refactoring to do, burning up time without generating anything cool. C’est la vie.

Today it was useful as it teased out my separation between things that move and are bound by pause mode (shots) and things that are mobile and can ignore pause mode (the camera, which I use for impromptu debugging once I’ve frozen everything in place). I need to make this a first-class feature.

3. Player-Obelisk collisions

This was the big milestone for today and I got there.

Again I got bitten by lack of typing in Javascript. If your function assumes that a passed object has an ‘x’ attribute, sooner or later you’re going to pass in something that doesn’t have an ‘x’ and NOBODY WILL TELL YOU. It will silently produce weird behaviour. More manual type checks needed.

I’m beginning to realise that making assumptions is a really bad idea in Javascript. For now the lesson is just to pass all parameters explicitly and don’t go digging them out of objects you know nothing about.

I was confused for ages because I was reflecting the Player (like a Shot) when they touched an Obelisk. Eventually the aha moment came, profanity was hurled and the Player now only moves, not rotates. Your ship now slides nicely past obstacles.

4. Sorting out the Camera-Player relationship

Currently the Camera sits inside the Player object, which is actually a wireframe sphere mesh. By sheer coincidence the front part of the sphere looks like a white crosshairs - a happy accident.

This gave me the idea for a 3rd person chase camera - I just need to pull the Camera back and follow the Player around at an offset.

Similarly I really like the idea of a minimap or radar that looks straight down on your ship with orthographic projection. Just add a second camera and look down on your ship, then render that in the corner. Should be useful for debugging also.

5. Achievement get

The camera is locked to the player without any ‘wobble’ in the wireframe.

The game is playable directly from github pages.

I’m using git exclusively from the command line with no crutches (i.e. the GitHub app). I even performed my first ‘git commit —amend’ and ‘git push —force origin master’ (naughty).

You can collide and slide around obelisks with a satisfyingly crunchy feeling and sound.

You can move and fly around in pause mode and check out what the shots are doing.

2013-12-09: let’s go Mr Driver - a chase cam

A couple of lines of trigonometry and dat.gui and we now have a chase camera, with a GUI dropdown to select it. Not something the original had!

The new camera gives a different feeling, a bit less claustrophobic and immediate.

The GUI is a bit awkward as it hangs on to focus when you click on controls - a minor annoyance.

It’s always gratifying when trig works out first time. As always it’s live and playable at http://air.github.io/encounter

trig notes

2013-12-10: GAME OVER

It’s really exciting to have all the basic stuff done, so new features now slot in rapidly.

The chase cam is now fully configurable. You can use the GUI sliders to pick your favourite height, distance and angle.

For the first time shots will kill you - including your own for the moment, so watch those shots next to obelisks.

We have a player death sound effect and the game stops in order to make you feel bad and hit reload. The sound effect i got in as3sfxr is actually not a million miles away from the ‘enemy killed’ sound in the original.

For a final touch this build has a shot spawner not far from your starting point to keep you on your toes. It actually feels a bit like a game now : )

2013-12-11: you spin me right round

Since the camera’s so easy to work with, upon your demise let’s switch to an orbiting camera that slowly pans around you. Reminding you of the folly of your existence.

orbit cam

Be strong. You will live to fight another day.

You can still mess with the camera after you’re dead (just like in real life).

Go observe at http://air.github.io/encounter

2013-12-14: kill it kill it dead

Tense one-on-one action against a ‘devastatingly intelligent’ enemy now at air.github.io/encounter.

A day of refactoring through a hangover to be able to plug in Enemy actions as well as your own. The main encounter.js is starting to slim down now and only handle the basics - a good sign.

I’m pretty amused at how the current AI works. Check out Enemy.js for a cheap laugh.

The end of the day was spent puzzling over OrthographicCamera and CombinedCamera. I have no idea how they work - a quick test produced extremely confusing results. Suspect I have to try them out on a super simple test case and get my head around that first. Maybe this link will help.

What I’d like to do is have a top-down camera option without any perspective skew, basically like a radar view.

Also today I got my first ever pull request on github! Thanks Texodus.

2013-12-17: multiplayer, why not

I started thinking about making a two-player game after reading the superb writeup of World Wide Maze.

As a ‘quick experiment’ I began messing around with Websockets at 7pm, and now just after midnight I have two Encounter clients updating each other in real time.

There’s a node.js server in between, streaming data back and forth. Most of the magic is in socket.io. The cleverness in the server is just keeping a single two-player game going by kicking out whomever the oldest player is, when too many people join : )

WebRTC would seem like a good choice for peer to peer, but a server component seems inevitable to do matchmaking.

This is all in the netcode branch so not live. And to be honest not really C64-esque at this point, it’s more of an experiment than anything else.

I’ve only tested on localhost so far - I imagine the performance back and forth to my EC2 server is going to suck enormously. Something to look forward to tomorrow : )

2013-12-19: light table from the top

So two cool steps forward today.

First, had some success working with Light Table. It’s cool because you get the browser and editor hooked up bidirectionally, with very little fiddling. It looks like this:

light table example

Whenever you make an edit, hit Cmd-R in the editor and the browser reloads. The console is there at the bottom.

While it’s running, you can highlight any variable in your code and Cmd-Enter it for a breakdown of the current values in the runtime.

It’s not quite as amazing as I want it to be. The eval popup doesn’t update with live values - you have to delete it and re-request, with an awkward right-click.

And there’s effectively zero documentation. But overall very compelling.

Second thing - I figured out enough of the Orthographic camera to get it functioning. It looks like this, once we dial down the playfield so it all fits on one viewport:

Very cool! This will make things like a top-down radar (hopefully) easy to implement.

2014-04-02: The continuing adventures of Encounter

I’m working on a game. It’s a remake of the C64 classic Encounter, but available to play in any modern browser (including your phone, eventually). This is made possible by some cleverness called WebGL.

Every so often I post some techie details about how it’s going. Previously I used tumblr, but why not have everything here? Much tidier. You can read the devlog entries so far by browsing the projects tag on this very site.

The easily distracted can play the current build here and browse the source if you’re suitably perverse.

So what’s been happening in 2014?

1. Thoughts on multiplayer

In December I had a pretty good shot at getting a two-player version running.

Even with a humble EC2 server acting as intermediary between the players, latency was low enough that this approach is definitely workable.

It turns out though that getting player movement to register on both screens was the easy part.

The hard part is getting two clients to agree on where Shots are, and what constitutes a hit. It’s not a new problem by any means, but without some pretty robust server adjudication and lag compensation, this was a GIANT NEON RABBIT-HOLE that I would never emerge from with sanity intact.

So that branch is left as a curio for now and onward we go to get the core game done.

2. State machines

As soon as your game reaches any complexity you realise you need to model state all over the shop.

There are macro states - are we in attract mode, or combat, or warp? - as well as littler state machines for the objects that do interesting things over time. Our Enemies need to decide if they’re moving, waiting or attacking. A Portal needs to decide if it’s opening, waiting or closing. Stuff like that.

How do you code that? With an FSM.

Apologies. A Finite State Machine.

It’s better to reuse than to invent, so off to github we went to find javascript-state-machine. Things looked rosy for about an hour of state fiddling, before I hit a brick wall. Javascript was throwing an obscure error in the depths of the library that I couldn’t make sense of.

After a few days of intense swearing I gave up and surfing on a tsunami of optimism, coded my own simple approach. The good news: it works nicely.

The FSM story finally got some closure thanks to the independent eyeballs of JHawk. It turns out that the OO parts - my pet hate in the land of Javascript - are absolutely fine and not the issue. Rather, I’d forgotten to have a one-time start state for the FSM to hold while init was taking place. So our poor FSM tried to jump into a functional attract mode state before everything was ready to go. By poor fortune the relevant error message was ‘a bit cryptic’ (i.e. abysmal).

So this FSM library is absolutely usable, but I’m some distance into my own implementation now. I could retrofit my various FSMs back into it, but the charming simplicity of the hand-coded approach is attractive.

3. Have you ever seen… a Portal?

With the FSM approach in my head, I bashed out the Portal.js to about 100 lines before trying it. A bold move. It worked pretty quickly with only a couple of tweaks to make it commit-ready.

When your Enemy counter - that’s the E1 in the top left - reaches zero, a Portal will open somewhere on the playfield. It animates over time using tween.js, a library which can’t easily be discussed without popping a few hundred boners. Check these graphs if you don’t believe me.

Get to the chopper Portal quickly though - you have twelve seconds before it closes and you’re thrown back into the fray.

4. Warp

It’s taken me a few iterations of brain-bending to figure out exactly how Paul Woakes implemented the intimidating warp phase. It seems clear that we can simulate the whooshing inbound-death effect by moving the player, but not the spheres of destruction. Since there’s no other visual reference point, the viewer can’t tell the difference. This also simplifies the update loop because the bad guys all stand still; we just force the player’s foot on the gas and let them deal with the consequences.

I think it’ll take a few tries to get it feeling right - I’ll update when it’s in a workable state.

n.b. Are they asteroids? In the source I’m calling them asteroids. The manual says: “You will be propelled at high speed through a hail of spheres.”

Well that makes perfect sense.

2014-04-03: Into the warp we go

With a solid evening’s work, we now have a functioning warp level. Hurray! You can try out this rough version at http://air.github.io/encounter.

It’s all an illusion

In the simulation we’re setting up a field of static spheres. They don’t go anywhere, but just sit there looking stoic. At the same time we disable the player forward/back controls, and use tween.js to ramp up the player speed as if they had their foot on the gas. The goal is to have the player kept busy dodging and feel a real fear of death.

Without a frame of reference, it looks like the asteroids are flying at you. Well. I think it does anyway. Being honest, once you know how it works you start to lose perspective : )

My god, it’s full of colours

The multi-coloured asteroids in this build I got for free. By sheer accident I discovered that three.js will give you a random matte colour if you leave the material undefined. An ideal quick hack to emulate the psychedelic original. To be completely accurate I need to randomize against the palette defined in C64.js, but you won’t tell anyone (ssh).

Hurt me plenty

The asteroid field is a super-naive algorithm right now. We simply loop 800 times and place the asteroids in a random location within 200k units of the player - basically forming a giant circle of death. If your numbers are unlucky you will spawn right in an asteroid and it’s game over. DEAL WITH IT.

A side-effect of this quaint approach is that you can actually emerge from the circle into free space, which is a bit dull. In the next iteration we need to dynamically create new asteroids in the path of the player as they weave about and keep them occupied for the whole warp run.

Collide me

Collisions are brute force. On every single frame, we just check the centre-to-centre distance between the player and every single asteroid.

There are a bunch of optimizations that spring to mind here - cleaning up asteroids as soon as they move out of the player’s sight, for example - but at the moment I can put 800 spheres on the playfield and the FPS is still solid. Hurray for fast Javascript engines allowing us to be lazy programmers!

Aesthetics

Finally I turned off the pseudo-crosshairs I’d been working with. This was another happy accident: By rendering the player sphere as a wireframe, the camera peeking out from inside happened to see a vertex directly ahead, giving us a super-cheap crosshair*.

* Crosshair singular or plural? Who could possibly say.

Alas the lines filled the screen and really messed with the cool darkness of warp - as well as obscuring asteroids far away - so it had to go. Now we don’t render the player at all unless we switch into an external camera, which only happens when you bite the bullet. Or indeed asteroid.

Stay safe out there.

2014-04-08: Let’s make the warp suck less

From our previous installment, we know that we got the warp stage throwing some spheres at the player with some pretty colours involved. The problem is that we’re creating a static playfield of obstacles, and emerging from the other side into safe, empty space isn’t fun. We need something more dynamic.

In this build, we create the illusion of a large continuous playfield by creating asteroids on every frame, and placing them only in the player’s field of view.

you really need to see this moving...

Here’s how we do it. On every update, we project into the distance along the player’s line of sight: say 15,000 units away on the Z axis. Then we take a random offset to the right or left. So that’s a translation on the X axis, somewhere between -15,000 (far left) and 15,000 (far right). Since our field of view is 45 degrees, these asteroids will definitely appear somewhere on the visible horizon.

If you visualise a T shape stretching out in front of you, we’re creating danger on the top bar of the T. Since the T shape is always projecting straight ahead, no matter where you turn we will create asteroids in the right place.

With some tweaking, the current version gives a passable warp experience. Crucially,

  • You can’t emerge into safety until the deceleration phase
  • Depending on your luck with the RNG it can be genuinely challenging
  • There is good tension at the start before you actually get into the action.

the original warp

It strikes me that the unbearable tension in the original game is mostly due to the horrendous wailing of your warp drive (it’s a warp drive in my mind. Who knows what the official explanation is). When you reach top speed on the C64, a solid atonal blare vibrates into your bowels and makes basic motor function difficult.

That’s a solid item for the TO DO list. For the moment I have no idea how I’m going to create continuous sounds in Javascript - like Warp, or the Player movement noise - while keeping the authentic synth effect.

To make warp a bit more intimidating in the meantime I inflated the asteroids to be sized at 150% of normal Shots in the world phase.

With an eye on detail, the way that the asteroid field is generated is a bit naive and needs improvement. Currently we create one sphere per frame. This means when you’re going slowly - i.e. the acceleration phase - the field is more dense for no good reason. Added to that, a low-FPS platform is going to have an easier time with a sparser field. So that’s another item for the TO DO: create N asteroids per second and don’t depend on the current FPS.

Lastly, there’s a pretty obvious bug right now for triggerhappy players. Any shots in flight when you enter the Portal will hang around and mysteriously freeze… then come back to life when you exit warp into the next round. This is because I don’t have a clean way to sweep the Shots specifically out of the State.actors array. Not a big deal but some refactoring needed.

Finally lastly, don’t tell anyone, but you can press K (mnemonic for Kill Evil UFO) at any time to zap the enemy and skip to the warp.

2014-05-12: Never forget that Javascript hates you

My background is OO programming. First with C++ in the early 90s, then the shiny Design Patterns book in ‘95, then years upon years of Java.

Strolling briskly into the world of contemporary Javascript with an OO mindset will lead you to a dark place of heartbreak and disappointment. As I noted in an earlier post, trying to do what I would call normal OO in Javascript is, like drunk blindfolded surgery, both messy and unsafe.

It’s not just that there’s a single potential error, as I’ve often read. The whole approach is tortured, error-prone and requires a slew of protective guard functions to stop you going off the rails. You can watch Douglas Cockroft talking about his issues with the JS language design and proposing workarounds.

The title of this post is stolen from this article bashing JS as a functional language. I can’t say I’m enough of a functional programmer yet to comment on its fairness but the soundbite is catchy.

There’s a certain school of thought - evidenced here on stackoverflow - that you should just back away from the new keyword and do things in a functional style. For this project that’s essentially what I’ve done. For the cases where I need to generate many instances of Foo, I define a factory function Foo.newInstance and encapsulate what I need there. I don’t use inheritance.

My view might change over time as I get more comfortable with the patterns, but for the moment it’s more productive to simply not think of JS as an OO language. Working in a purer functional style is actually very refreshing. I found that not spending hours agonising and revisiting complex super/sub relationships results in clearer code.

Let’s clean up this town

Now that I’d decided conclusively that the THREE style of OO was a terrible and confusing style to emulate, I began ripping out all the faux-OO code.

I think there’s only one remaining use of this in the codebase, which is well understood - attaching an update() function to a new Shot object.

This was a good time to clean up some oddities dating from the early days when the overall structure wasn’t clear yet. So the core actors[] array now belongs to State for example, rather than awkwardly sitting in the main encounter.js.

There is no spoon()

During my research I also discovered there is a fun difference between function declarations and function expressions. That should be pretty simple to understand right? Regard the length and detail in that answer and form a view.

Just another bit of nuanced JS magic I need to understand and refactor at another time. The immediate cost of my ignorance is that I’m missing a bunch of semicolons at the end of function expressions, as shown below.

// a function *declaration*. No trailing semicolon
function log(msg)
{
  console.log(Math.floor(clock.oldTime) + ' ' + msg);
}

…versus

// a function *expression*
UTIL.platformSupportsTouch = function()
{
  return 'ontouchstart' in window;
} // technically a needs semicolon here

It is not surprising to me that there are so many average Javascript programmers out there. For a beginner-friendly language, the number of pitfalls and foggy areas is nightmarish.

Given that, I think my self-imposed emphasis on code clarity is paying off. I committed many aggressive changesets that worked first time.

At the end of the overhaul, I was delighted to take out my comment reading, // FIXME what a piece of shit object model

100% statistics

I spent some time ditching the basic STATS widget and wired in the quite lovely rstats alternative. This gives you a compact graph of performance over time and some basic threshold support to make it go red when your FPS dips.

The THREE plugin seems quite useful, counting geometries, faces and the like. When I get to FPS optimisation on mobile devices it will pay off. For the moment though I need the screen space back, so it’s turned off. Not before raising an issue about the weird hard-coded alert once your scene breaks 1,000 WebGL faces.

Next up: creating touch support for mobile devices like my Nexus 5.