Jeff From Accounting - A Retrospective [Code/Design]
Why hello there reader!
Following is a brain dump from my perspective of making Jeff From Accounting.
@hi-im-greg has covered a lot of the art process in his post which you can find here - a recommended read if you wish you know how to do a jam after having appendicitis... sometimes I think that boy is actually a machine.
I’ll be digging into the game design, gameplay systems and technical challenges I dealt with during the jam.
The Unity project - including code and assets - can be found on github here
[Spoilers] If you've not had a chance to play our game yet check it out here
Day 1
We started pretty late on saturday - around 12pm - with a coffee and fryup at a local café. Over food we discussed the theme but initially we were frustrated by the inherent juxtaposition of combining impatable things. We toyed around with some ideas we had pre-jam in unlikely pairings such as making a Dungeon Keeper clone rhythm game, but nothing really felt right. Greg was keen on making an FPS, something none of us had done before and I had been thinking about typing games like the classic ZType and Typing of the Dead. So we spun on that for a while and out popped the idea for reloading what you type, which led us to a typewriter gun and subsequently the office theme. Offices are boring though, and inspired by Davey Wreden’s excellent ‘The Stanley Parable’ we decided to go for a surealist styling.
Our work was kind enough to lend us office space for the jam so we cosied up in a meeting room and assaulted the whiteboard for an hour, sketching out the floor plan and trying to scope out features.

After setting up the github repro and doing a bit of IT Crowd style admin we set off to work. Wanting to get something quick and dirty up and running for Greg to be able to move around the environment he was building I started with grabbing the default Unity StandardAsset FirstPersonCharacter. This turned out to be a total letdown, and I ended up spending a large portion of the day rewriting chunks of it, adding enumerated states, head bob blending, refactoring the step cycle to reset correctly on idle and rewriting Get functions that modify state.
(╯°□°)╯︵ ┻━┻
Next up was the weapon and damage system, I’ve written damage systems many times before but as we wanted to shoot letters I had to build around string buffers and character parsing to allow for non alphabetical skipping (spaces in names for example) and case insensitivity. This was relatively simple and pretty fun to write, and it was quick to get Greg’s first pass of the weapon in afterwards. Unfortunately I didn’t have time to finish writing the projectiles so by the end of the day we had some menacing looking enemy capsules with health, but no way to hurt them!

Day 2
Dale had finished the first bit of music and a load of sound effects, while Greg had roughed out Jeff’s office by the end of the first day, so my initial priority sunday morning was to get these setup up properly so we could get a feel of the character in the environment.
After helping Jeff find his office I moved on to reloading. I expected this to take ages as Greg wanted all the keys to move… but as it turns out I can write some pretty dirty code when I want to. Key GameObjects were found by name and bound to Unity KeyCodes on the assumption that they start with the prefix “Key_”.
KeyCode alphaStart = KeyCode.A;
for (KeyCode alphaKey = alphaStart; alphaKey < alphaStart + 26; alphaKey++)
{
Transform keyTransform = children.Find(t => t.name.StartsWith("Key_" + alphaKey));
if (keyTransform)
{
GunKeyBinding newBinding = new GunKeyBinding();
newBinding.keyCode = alphaKey;
newBinding.transform = keyTransform;
newBinding.originalZ = keyTransform.localPosition.z;
gunKeys.Add(newBinding);
Debug.Log("Found key: Key_" + alphaKey);
}
}
Using these bindings I could then loop over Input.GetKey(keyCode) and animate the keys.

Once we had a functioning typewriter it was time to start shooting things!
My first pass implementation for the projectiles was rough and ready, firing text meshes from the muzzle of the gun and ray casting delta moves, getting Health behaviours from the hit object and applying damage. But not having made an FPS before there were a few classic mistakes I made... more on that later, I could now shoot things!
So I grabbed a chair from Jeff's office, made it an enemy, and called it Greg Greg Greg Greg.

The rest of the day was spent tinkering with HUD, Text and weapon shaders, more player movement tweaks, a Music+SFX fading/queuing/pooling system and implementing a GameManager to help control some of the the more global game logic. I also hacked in a UE4 style "spawn at scene camera" helper to speed up game play iteration time.
Day 3
Monday was a late start and coffee was essential, so we made sure to stock up before heading into the office. By the time we all made it there things were feeling a bit tense; I had the core combat done with basic path finding but we were miles behind on what we wanted visually and Greg had only finished modelling the office, corridor and lift. Audio was in a good place though, Dale had been firing SFX at me faster than I could put them in the game and he had already written the elevator music. So I took that as a sign to get cracking on the "elevator experience".

We wanted the elevator to act as a calm before the storm, after the first bit of corridor combat against the now family of chairs. It would allow the player to take a breather, enjoy the surreal decor and absorb the mechanics they had experienced so far, ready for the next bout.
I'd not used Unity's timeline before and was surprised at how flexible it was, setting up trigger volumes, sequencing the audio, animating the doors and hooking up music queuing took less than an hour! I'm looking forward to using this more in future projects.
After a quick chat over yet more coffee we decided to scope back our original plan to have smaller rooms off of the main chamber so we could focus on polish. So Greg finished off a quick pass on the floor geometry and I rebaked the navmesh and started setting up enemies in the level.
Next I went back to the gun play, added a reticule and raycasts from the camera to resolve fire direction, but quickly ran into what I now realise is a classic FPS problem, if the muzzle is inside something your bullets shoot backwards! As we rendered the player in a second pass this was initially hard to debug as visually the gun was not intersecting anything. In the end I wrote some debug rendering for the projectile motion and hit locations, which highlighted the problem immediately - bit of a face palm moment really...

The next few hours are a blur, lots of stuff broke, got fixed and broke again. Many tables were flipped, more coffee was drunk and finally, miraculously we squeezed out a build for submission.
Post Jam Feels
For a start we were all exhausted, but more than that, proud. It wasn't quite as epic as we had planned in our heads, but giving ourselves time to polish made a big difference to the final presentation and allowed us to address more subtle niggles like text fading, prompts for reloading and lighting.
As I alluded to earlier the projectile implementation was fairly naive, and resulted in a frustrating bug where projectiles would sometimes pass through enemies - something a number of commentators and streamers picked up on. After a day or two of R&R I got a chance to look it over and discovered the issue stemmed from a subtle and annoyingly simple issue with the motion cast.
If an enemy was moving towards the player and the projectile stopped just short of hitting it, the next frame the cast might start within the enemy's collider, resulting in no hit response from the raycast. I feel like this is probably a typical problem with simulated bullets and my quick solution was to offset the trace origin backward by the delta move each frame. Of course this might result in incorrect hits against bodies moving perpendicular or faster than the projectile, perhaps solved by a secondary cast backwards, but a quick fix was all I needed :)
``` private void FixedUpdate() { float distanceToMove = speed * Time.fixedDeltaTime; m_distanceTraveled += distanceToMove;
if (m_distanceTraveled > maxDistance)
{
OnExpired();
}
Vector3 castMoveDelta = m_castDirection * distanceToMove;
Vector3 traceOrigin = m_castPosition - castMoveDelta; //we trace from one frame back to avoid missing objects coming towards us
m_castPosition += castMoveDelta;
float castDistance = Vector3.Distance(traceOrigin, m_castPosition);
//do cast
RaycastHit hitInfo;
bool validHit = Physics.Raycast(traceOrigin, m_castDirection, out hitInfo, castDistance, hitMask);
RaycastHit damageHitInfo;
if (Physics.SphereCast(traceOrigin, damageRadius, m_castDirection, out damageHitInfo, castDistance, damageMask))
{
hitInfo = damageHitInfo;
}
if (hitInfo.collider)
{
OnHit(hitInfo);
}
} ```
Conclusion and Thanks
We have been completely overwhelmed by the feedback you guys have given us and would like to take a moment to thank you all not just for your kind comments but also for the critical feedback on our and every other game this Ludum Dare. This will have been @hi-im-greg and my 7th Jam together, my 13th since LD26, and @dale-smith's first ever!
Lumdum Dare is a fantastic opportunity, pulling us back time and time again to help us grow and test both our creative and caffeine consumption limits :)
We are hugely grateful to all the hard work put into the event by organisers and admins behind the scenes.
And until next time, may Sittatron the Magnificient First of his Name keep watch over you all!
Aaron


















