New projects



I haven’t worked on anything major since I finished Spamocalypse – I just needed a break, and work has been burning me out a little in the meantime. However, I’ve started working on two new projects, trying to decide which I like more. One is best described as a smaller-scale version of Hearts of Iron, and the other is a real-time tactics game. Both are set in a fictional world I’ve had kicking around my head for a few years now.

The world
The original premise of this world is that some hills in my part of Ireland were once inhabited by dwarves, or humans who started living underground during the Iron Age. However, during the mid 19th Century, something goes wrong, and the whole area turns into a S.T.A.L.K.E.R.-style forbidden zone. Among the many, many things that have changed is that humans in the affected area have turned into orcs (AKA Fomorians…or trolls).
Galway is controlled by the Anglo-French Empire, the result of the British and French forming a military, then political alliance in the face of the Habsburgs of Spain/Austria forming a superpower. The Firtollán League, the alliance of the dwarven city states of Ireland, has been forced to ask them for help in keeping the Fomorians locked up in the peninsula.

The first one (Governor) player is the recently appointed governor of Galway, some time around the turn of the 19th/20th century. After a long, distinguished career in the Anglo-French colonial administration, Galway is your final posting before retirement. But can you last that long? All you have to do is approve various budget decisions, and try to avoid being sacked.
This would be based on managing several different factors: your popularity with the locals, the opinion of the press outside Galway, what the dwarves/Firtollan League thinks of you, your budget and your troops. The first three influence what your superiors think of you; if that becomes too low, you will be sacked, forced to resign or…forced to face a Full Independent Inquiry!

Tactics game
I was originally going to do a turn-based tactics game in the style of Jagged Alliance 2 or XCOM, but I’ve changed this to a real-time version. The only definite part is that it will build on what I learned from Spamocalypse – in particular, planning out systems so I can test more efficiently, using mock components. However, it will probably focus on hunting various monsters, including the Fomorians. I’m leaning more towards this one, purely because I can recycle some of my previous AI code.

So, it’s taken me three months to write this. I should probably decide on one of these and get on with it 🙂

Spamocalypse Postmortem

So, I finally released Spamocalypse last week, after two years of working at it in my free time. Now is as good a time as any to look back and see what went well, what didn’t, and what I would do differently.

Original plan
There were three goals I set myself when I started. The first (LOS) was to figure out how to make the bots see the player, while taking lighting into account. The second (Sound) was to figure out how to make them hear the player, and the third (Brain) was to find a way to make different NPC types react differently to specific stimuli. All of these have actually been achieved, though not without some work.

My original method for determining if the bots could see the player was to use an extruded box for their vision range, and storing the lighting inside a customised pathfinding system. It worked at first, but there were two problems: twenty or more MeshColliders in a scene for line-of-sight checks is expensive, and converting the player’s position into a Node consistently took about four milliseconds, which slowed the main thread down enough to be noticeable.
My fixes for these were to use capsule colliders for the humanoid NPCs, and to create a physics-based light calculation mechanism. Capsule colliders involve only two distance checks (one for the radius and one for the height), as opposed to the eight required by the extruded box (one for each vertex). The physics-based LightRadius mechanism is based mainly off trigger colliders and raycasting, which is considerably faster.
This did require ripping out my pathfinding code and converting the AI to use Unity’s builtin NavMesh. However, that works a lot better, so I probably should have done that from the start.

The sound detection originally used an octagonal mesh to manage their hearing range. I made their ability to detect the player be inversely proportional to distance, giving the player a chance to avoid them at longer distances. That part has not changed. I also originally made them only react to Sockpuppets, but when I started adding other alert sounds, I realised that a ScriptableObject was the ideal way to store these. From that alone, I’ve learned how to use Unity’s ScriptableObjects for holding common data.
The one major performance change I made was to not use OnTriggerStay for processing sound events. OnTriggerStay runs on the physics timestep, which by default is every 50th of a second. However, I found it ran much better if I ran it on a Coroutine every 5th of a second.

Originally, I used a C# delegate to distinguish between the different AI responses. Delegates in C# are a way to call a different implementation of a method at run time; in this case, each NPC type had a different search and attack method. However, this became a bit too convoluted when I started adding different effects: the bots were supposed to play smoke when moving, the spammers are supposed to vomit when attacking, and so on.
Currently, moderators and bots are subclasses of the main SpammerFSM class. The only difference here is that their search and attack methods override the search and attack methods in the SpammerFSM, allowing me to subtly change how they react to a sockpuppet, or when they start attacking. However, it’s still a bit clunky. I think that for my next project, I’ll use interfaces instead, which should allow me to customise the NPC types more effectively.

What worked
So, apart from the three goals I set myself, what worked? Well, I found a more efficient way to calculate light intensity. I figured out a basic way of doing player objectives, and I found some basic mechanisms for making it clear that the player can interact with something. All of these are going to be useful in future projects.

What didn’t
The biggest problem is that I had too much scope creep. I kept thinking of new ideas to add in, each of which added their own bugs. The project just became too damn complicated for me to test by myself. Which leads me into the next problem: I have a problem getting people to try it. I hate nagging people, so I tend to just announce projects and see if anyone plays it. I still don’t know if the Mac build works! (That said, the analytics on GameJolt tell me that I’ve had 3 downloads out of 23, with no complaints…)

What can I do about this
The first thing I could try is to scope things better, i.e. decide what I’m actually going to do. That’s something that will probably come with practice. I deliberately refused to give myself any deadlines, mainly because my day job has some pretty unrealistic tight ones, but I may have to consider this.
Another, more concrete thing would be to try decoupling the systems. For example, the SpammerFSM class and its subclasses rely on the LevelManager class – but that has to include objectives for a level, which makes testing the AI in isolation tricky. A way around this would be do what I did for the light calculation: create an interface that defines what methods the class will have. The key thing here is that any class that implements it will include those methods, but the exact details will vary.
I’m also thinking of setting up my own Git server for source control. Source control is basically a way of keeping track of who changed which line of code, and when. In particular, that would have been very handy for figuring out when and how I made the NPCs deaf for over a month! I do have some stuff on GitHub, but I’d prefer to keep my full projects private for now.

So, there’s some things I can improve, but I’d say this was pretty successful overall. Not least because I actually finished it!

Spamocalypse release 1.0.0


, ,

Finally, two years since I started working on it, Spamocalypse has reached version 1.0.0. My brother tested 0.8.1 and made some suggestions, some of which I have implemented:

  • The map and notes have an alternative method for closing. You can still close them using the “close” buttons, but pressing Escape or F/M will now do that as well
  • I finally fixed a bug in the camera rotation, which would have resulted in the player appearing to stand upside down. One of the current known issues is that on my Linux machine, camera rotation using the mouse is locked to the screen, so I didn’t realise that this could occur. I have fixed it, though he found it amusing.
  • The knife animation was too short, i.e. it didn’t travel far enough. I’ve increased the thrust length so it is a bit easier to use.
  • He also mentioned that it was hard to aim the SQLer when the spammers are moving laterally. However, that’s due to the SQLer rounds being physics-based, so the player would have to manually aim it to account for this. I haven’t implemented this, but it might come into future games.
  • I increased the Moderator’s turning speed while trying to investigate an collision issue with the spammers. I couldn’t reproduce that issue, but make the Mods turn faster will make them react faster to the player
  • .

Anyway, this is the official release of Spamocalypse. Here’s the release trailer:

Spamocalypse 0.8.1: last build before release

I’ve been working on Spamocalypse for two years. I’ve run out of things to add…so this is going to be my last test release before the Big One.

Most of the changes are fixes for gaps in the scenery, and rebuilding some models so the nav mesh could go inside some buildings. I’ve also updated the player’s footsteps so sprinting and walking have different noises, and the SQLer aiming camera is a little higher so it doesn’t clip through the stock.

Spamocalypse 0.7.2: NPCs aren’t deaf any more


, ,

I am an eejit. While trying to fix the performance issues a month ago, I think I broke the spammers’ sound detection. However, I didn’t realise this until I tried making them go on alert if they heard a SQL round miss. I’m not even sure if that’s when it happened!

The root cause of this bug is that I had converted their sound detection to use a Coroutine with a 0.2-second interval, rather than OnTriggerStay. OnTriggerStay runs on the physics timestmp, which by default is 50 times a second – which is a bit more intensive than I wanted. However, when converting it, I forgot to actually make it run more than once! So, the spammers were effectively deaf, which pretty much defeated the point of the game.

Another thing that was broken was their Sockpuppet detection. This was a physics issue – I had set the Spammer layer to ignore collisions with the Decoy layer. However, that’s the layer which is supposed to contain the Sockpuppets!

There are two things that would have helped with this:

  • Automated testing. I might have noticed this if I had a dedicated scene where I can set some bots to detect and attack things in a controlled setting. This
  • Source control. Why should I try remembering what I changed last week when the computer has a record of that for me? I have some familiarity with Git in my day job.

Spamocalypse 0.6.1


, ,

Finally, I have a new build to show off. I originally built this on Sunday and uploaded to GameJolt, but when trying to take some screenshots, I found a few bugs that required a patch.

Bug fixes

  • I converted the colliders for line-of-sight checks to use Capsule Colliders instead of Mesh Colliders. The Museum level had almost 30 MeshColliders for this, which really screwed up performance. The other levels were okay, but Capsules covered roughly the same area as the extruded box I was using. I really should have properly tested that sooner.
  • I also fixed a long-standing bug in the line-of-sight code that caused the spammers to spot the player as soon as they came within 5 metres – regardless of how bright the player’s surroundings are.
  • I removed the directional light from the Museum level. The component I was using to calculate its effect on NPCs and the player was ignoring the colliders on the upper levels of the museum, which resulted in the player being “in the moonlight” while indoors, in a windowless room. There’s only one part that’s outdoors in that level, and there’s other lights there anyway, so I didn’t even need it!
  • There was a note in the Inner City level which wasn’t assigned – this caused the game to freeze up when the player tried to read it. Game breaker!
  • The player’s compass in the Inner City level didn’t rotate properly. I had assigned it to the UI layer in an attempt to stop it from clipping through walls, but the camera that handles UI doesn’t rotate. I may add this back in the next build – I think the idea is sound, I just didn’t do it properly

New mechanics
As if I didn’t have scope creep already, I went and added more:

  • Bushes. Yes, you can hide in bushes now. They rustle if you move into them too quickly, so no running and diving into them!
  • Light switches. I should really have added this sooner, but I didn’t think of it until I started testing it last weekend. It meant changing the lighting from baked to real-time, but that does mean I can get shadows from NPCs, so it’s good.
  • Firewall controls now include a sound effect and light to make it clearer that they’ve been used.

Known issues

  • The SQLer doesn’t quite aim where the player’s looking. I am considering adding a secondary fire mechanism that would allow the player to aim down the sights.
  • Occlusion culling might be inaccurate in one or two levels. This results in buildings “disappearing” while you’re looking at them. I think I fixed this in the original build on Sunday, but this is present just in case.
  • The museum map doesn’t include the new garden on the upper floor.

Spamocalypse 0.5.0: new level, stats, and artwork.


, ,

Finally, I have a new build, which brings Spamocalypse to 0.5.0. The changes:

  • I added the final level. This uses the same scenery as the first (non-tutorial) level, but there’s no loot to steal this time – just get to the sewers where you started!
  • I also added a credits scene (see below for a screenshot). This will display your total stats over the course of the game – number of deaths, kills, loot stolen and how often you were spotted.
  • I finally fixed that bug where you could lean over while jumping. It wasn’t a game-breaker, but it looked bloody weird.
  • Some general artwork updates. In particular, the SQLer now fires darts instead of blobs. They still use a sphere collider to handle physics, but I figure that will do.
  • Refactored the patrol points so they have a component attached to them. This component, PatrolPoints.cs, just holds their position, purely so I can filter through the scene more easily.

Here’s the credits screen. You can skip the credits and stats by pressing “Escape”

The next stage is to create more props for the levels, giving the player more places to hide or things to look at, then any bug or UI fixes I can think of. Nearly two years in development…I might actually finish this after all!

Building multiple platforms with one click


, ,

EDIT: this is now up on GitHub.

I finally got tired of manually changing platforms after creating builds, so I went and created a customised BuildPipeline. This creates a 64-bit Linux build, then switches to 64-bit Windows, and ends with 64-bit Mac OSX, all with one click of a button. I wish I had known how to do this earlier!

The basis of this is manually calling BuildPipeline.BuildPlayer. This requires a few parameters:

  • An array of strings with the names of the levels you want to include
  • The name and location of the executable (i.e. what the players are going to click on)
  • The target platform (in my case, 64-bit standalones for Linux, Windows and Mac).
  • The BuildOptions. This is the tricky one: according to the docs, it is possible to combine multiple values of this, but I haven’t figured out how to do this yet. It might be because I’m on 5.3.6, instead of 5.5.

And that’s it. Here’s what the code looks like:

using UnityEditor;
using UnityEngine;
using System;

public class MyBuildPipeline {

// constants for defaults
const string WINDOWS_FILENAME = "_Windows";
const string LINUX_FILENAME = "_Linux";
const string MAC_FILENAME = "_Mac";
const string SCENE_FOLDER = "Assets/_Scenes/";
const string BUILD_PATH = "Player Files/";

// TODO: find how to combine these
static BuildOptions devOptions = BuildOptions.AllowDebugging;

public static void Build()
Debug.Log("Creating new builds");
var levels = new string[6]{
SCENE_FOLDER + "Preloading.Unity", SCENE_FOLDER + "Main Menu.unity", SCENE_FOLDER + "Sound Test.unity",
SCENE_FOLDER + "City Inbound.unity", SCENE_FOLDER + "Inner City.unity", SCENE_FOLDER + "Museum Model.unity"

// calculate the build date
DateTime date = DateTime.Now;
string buildDate = string.Format("_{0}.{1}.{2}",date.Day, date.Month, date.Year);
Debug.LogFormat("Creating new builds on {0}", buildDate);

MakeLinuxBuild(levels, buildDate, BUILD_PATH);
MakeWindowsBuild(levels, buildDate, BUILD_PATH);
MakeMacBuild(levels, buildDate, BUILD_PATH);


/// Makes a Linux build.

/// Levels to build.
/// Date string.
/// Path.
static void MakeLinuxBuild(string[] levelsToBuild, string dateString, string path)
Debug.Log("Creating Linux build");
string fileName = LINUX_FILENAME + dateString + ".x86_64";
BuildTarget target = BuildTarget.StandaloneLinux64;
BuildPipeline.BuildPlayer(levelsToBuild, path + fileName, target, devOptions);


/// Makes the Windows build.

/// Levels to build.
/// Date string.
/// Path.
static void MakeWindowsBuild(string[] levelsToBuild, string dateString, string path)
Debug.Log("Creating Windows build");
string fileName = WINDOWS_FILENAME + dateString + ".exe";
BuildTarget target = BuildTarget.StandaloneWindows64;
BuildPipeline.BuildPlayer(levelsToBuild, path + fileName, target, devOptions);


/// Makes a build for Mac devices.

/// Levels to build.
/// Date string.
/// Path.
static void MakeMacBuild(string[] levelsToBuild, string dateString, string path)
Debug.Log("Creating Mac OSX build");
string fileName = MAC_FILENAME + dateString + ".app";
BuildTarget target = BuildTarget.StandaloneOSXIntel64;
BuildPipeline.BuildPlayer(levelsToBuild, path + fileName, target, devOptions);

I’m considering adding this to GitHub, once I’ve figured out the BuildOptions.

New paths through levels

Now that ladders, lifts, etc. are in the game, I decided it was time to actually use them.

I’ve rebuilt the start of the first non-tutorial level so you have to climb out of a sewer or a pipe. In the previous version, the sewer was just a pipe at the same level as the street, which just didn’t look right, but in this version, it could be a maintenance tunnel to a canal, etc. You can also climb onto one of the roofs around the level, and then use that to avoid the spammers.

The second level now has a sewer that leads to a dungeon under a police Moderator station. Bit of a design flaw there, but it does get you past a spambot that’s too dumb to move from it’s junction. Yes, I know they weren’t meant to be particularly bright, but this is taking it a bit far!

I am thinking of adding some rooftop paths, again to add some variety, since this level has only two roads you can follow. Perhaps a fire escape into an apartment for a certain objective would lead you out across the rooftops…

I also rewrote the briefings for the levels slightly, trying to make them sound more like Garrett from Thief, and I’m considering recording them. The next upload is going to be a sample recording…if I’m not too embarrassed by my own voice.


Spamocalypse Updates: Mac build, bug fixes, new GUI


, ,

Less than a week since the last build, and I’ve had some feedback from several places.

Firstly, I now have a Mac build. This is completely untested – I don’t have a Mac myself to test it on, but somebody commented on the Feedback Friday thread that they couldn’t test Spamocalypse because they were on a Mac…so, I figured why not. Just don’t blame me if it breaks! 😛

New UI
The biggest criticism I had on the thread was that my GUI was awful – it was basically white rectangles that turned orange when highlighted. It mostly worked, but the style just didn’t match the game. So, I reworked the GUI completely so that the menus look like a piece of parchment, with red wax seals representing the buttons. It’s not 100% complete – I may rework the new briefing menu so that the text is over multiple pages, since I’m using a book as the background – but here’s a pair of before-and-after screenshots:



Bug and mechanic fixes
It wouldn’t be an “Early Access” game without these! So, here’s the list of stuff I fixed.

  • I forgot to assign some buttons for the game over screens. It basically looked like the game had frozen.
  • I finally fixed an issue where you could jump while leaning. This didn’t break anything, but it looked weird to be leaning at a 30 degree angle while jumping.
  • The way ladders used to work was that they would turn the player’s Rigidbody kinematic, and convert forwards/backwards movement to up/down movement, while leaving sideways movement unaffected. I’ve changed this to make the player’s Rigidbody ignore gravity instead, so that they won’t go through an object when using it. I’ve also added a tutorial message explaining how to use it.
  • One of my workmates thought the exhaustion mechanic needed to be refined. I’ve tweaked it so you can’t run, jump, attack or climb a ladder while exhausted, but you can move…albeit at a snail’s pace. I may adjust the speed later