Month: September 2023

How to Write a Game in Under 13 Kb While Taking Care of a Baby – REMIX

If you don’t want to read the “intro” and go directly to the nuts and bolts, that’s ok.

First of all, yes, this title is 100% stolen from this awesome post by Jaime Gonzalez. I read his post a couple of times in total awe of his 13 kbyte creation, and it inspired me to join the JS13K game jam. My intention isn’t to suck the SEO juice from his post but, even though I don’t know Jaime, I could feel his pain … I am also a dad to a 8-month old little guy that means the world to me. Taking care of a baby is fascinating, challenging, and above all, it is exhausting! The one bit of energy left after he goes to sleep must used for work … and then I had to squeeze a last tiny bit for the jam!

Needless to say, it was tough, but I made it! So, thank you for inspiring me, dear stranger of the net.

I had already participated in another JS gamejam (here’s my entry), but I thought the “open ended” nature (any engine, any size, any team size, any anything) of a traditional jam wasn’t really my thing. As a lone ranger, it is rather difficult to produce quality and polish in JS compared to teams using Unity for example. Don’t get me wrong here though, there are some awesome games produced by a single developer, but I much rather do the heavy restriction of the 13Kb game (that’s what js13k is about by the way).

On to my game: Angry Temujin.

Genghis Khan
Genghis Khan

See it live here: https://js13kgames.com/entries/angry-temujin

The theme for this year’s jam was the thirteenth century, which immediately brought me to Genghis Khan. I’ve always been in awe of this man. The mongol empire was one of the biggest, if not the biggest, empire the world has ever seen. Interestingly, but not surprisingly, his tactics were brutal but effective. In the siege of Nishapur the whole city was destroyed, its citizens murdered, even cats and dogs were killed so there would be no trace of it.

Not a nice guy. Another interesting tactic was to just marry (it is more complicated than that). And there was a whole lot of that: https://www.iflscience.com/fact-check-are-one-in-200-people-descended-from-genghis-khan-65357.

So, yeah, this human was special. But he started alone. He was born Temujin (Genghis Khan, is a title, not a name) and, in fact, when his father died from poisoning, his clan left him, and his family, out for the wolves. Needless to say, he made a comeback.

I wanted to capture that idea … start as one and build an army as you go. At the same time, I was totally and utterly captured by awesome simplicity of Vampire Survivors. What if I could create a weapon system like VS but instead of killing enemies would transform them into troops?

I went along with that.

The rules of engagement

  • I wanted to do this using ECS (a new thing for me).
  • I wanted to use Behavior Trees for controlling friendly units.
  • I wanted to use esbuild (a new thing for me as well).
  • I did not want to do weird JS tricks, bad variable naming, unreadable code, etc … code golfing.
  • I wanted to write solid, readable code, maybe even document it.

Code Golfing

Just … no … Don’t do it.

I can see why people do this. But there is a whole lot of value in understanding what you are actually writing! Clever lines of code make for horrible debugging and completely break the flow of reading the code once you are past the “optimization”. When you are fully done with the game, and are now extremely close to the 13k mark. Sure, try to golf a few things, but the gains from this are just not worth it. I rather you spend the time making the rest of the code better that optimizing one thing. Keep in mind, the code will be minified and mangled, so most of it will be a whole lot smaller anyway.

If you MUST use code golfing, do it … but I didn’t, and wouldn’t.

ECS

Ah! The holy grail of AAA game development. Entity Component System is a type of architecture where your game is split into 3 different components: entities (basically an id), components (the data), and systems. Systems do their work in components and store data in them. Entities are just a way to relate things. This allows amongst many things to code something once and apply it to different things. For example, I have a keyboard control system, which works on entities that have the keyboard control component. In my case, the player entity has a keyboard control component and is how it moves. But I can just as easily add it to an enemy, or a tree, or a bullet, or any entity!

But, in javascript? well yes! There are a couple of libraries around, most notably ECSY. It is fine, but the footprint is too big. There is also the excellent https://github.com/kutuluk/js13k-ecs but I didn’t like that selection was additive only, or at least that’s what it looked like to me at the time. So I just wrote my own.

One of the reasons ECS is the holy grail for game development is performance, but I feel that this critical piece is almost gone in JS. The magic lies in avoiding CPU cache missed, but in JS we don’t have access to memory allocation. You cannot guarantee where your data will be in memory. I guess the closest thing would be to create an array of elements and pre-fill it with data, but I hate this approach. I am not a down to the bits JS guy, so this is just my opinion. Please correct me if I’m wrong.

My implementation is object-based and rather naive, but it works! here is what an entity would look like:

class Entity {
  addComponent( key, component ) {}
  removeComponent( key ) {}
}

In hindsight, this was not great … why should a component have a key? but we keep going. This is what a component looks like:

const BodyComponent = function( width, height ) {
	this.width = width
	this.height = height
};

export default BodyComponent;

To add body to an entity we would:

const exampleEntity = new Entity().addComponent( 'body', new BodyComponent( 16, 16 ) );

There is an entity manager and a component manager that handle creation and querying of data. So a system would do:

class MyPhysicsSystem extends System {
  update( delta ) {
    this.componentManager.query( e => e.components.has( 'body' ) ).forEach( entity => {
      let body = entity.components.get( 'body' );
      // do something with the body component
    } );
  }
}

The whole thing works, and thanks to modern computing, it works fast, but I took a bunch of shortcuts that I’m not too proud of:

  • Entities as well as Components MUST be pooled so the garbage collection doesn’t kill you, I didn’t.
  • Queries are run on every iteration even if the results haven’t changed. This is highly inefficient, there should be some kind of query caching or pre-work done so we don’t have to iterate every time. That said, this was querying 75.000 entities at 60 fps. Not incredible, but more than enough for my 13k game. At most I had 30 thousand entities …. so, not a problem.
  • I already said it, but adding keys to components is just silly.

It is small. It worked. That’s all I needed.

I cannot stress this enough: calling ECS the holy grail, doesn’t mean there aren’t other ways … in fact, I think object oriented would have been better in this case! but I wanted to give ECS a shot. It is a completely different mindset though. It was strange. It was complicated.

On the bright side, having systems completely isolated from everything else was really nice! each system does one thing, and because of that, they are super simple to debug.

Behavior Trees

Making NPCs act interestingly can be done in a couple of ways. The more common approach is FSMs (Finite State Machine), for simple behaviors, and for more complex ones Behavior Trees (right more here). There are other ways of course, but I find these two easy to understand.

At first I was going to use BehaviorTree.js which I had used before. I had done some simple trees and was cruising on the testing. When I built the project I realized that while great, it was just doing too much, and the exports were not being eliminated by tree shaking. In other words, footprint was too big, can’t fit on 13kb. So I ended up rewriting the whole thing using a similar API. Even though I didn’t have enough time to actually make the units have interesting behaviors I am very proud of this particular btree implementation. It is short and sweet, and works like a charm. Exactly what you need for this jam. I will probably release this as a stand-alone. The whole thing can be found here.

One thing that I should mention, and in the spirit of being self-criticizing, the implementation of the Cooldown decorator is rather lazy:

export class CooldownDecorator extends Decorator {
	constructor( args ) {
		super( args );
		this.cooldown = args.cooldown;
		this.timeout = false;
	}

	step( blackboard ) {
		this.status = FAILURE;
		if ( ! this.timeout ) {
			this.timeout = setTimeout( () => {
				this.status = this.child.step( blackboard );
				this.timeout = false;
			}, this.cooldown );
		}
		return this.status;
	}
}

Notice the use of setTimeout … it is just not good. Imagine you throw a fireball, now the cooldown is on, now you pause the game and just wait. When you resume, the cooldown would have expired! Not great. The right way would be to pass or check the game’s timer/delta and compare your current time versus the cooldown requested.

The build tool

This was one of the simplest parts of the project, but also one I truly enjoyed making.

I wanted to give esbuild a try. If you have ever been annoyed by slow build times with webpack or other bundlers … you’ll be in for a treat with esbuild. It is SO FAST!

The process is actually quite simple: esbuild bundling the code and assets, the result is pretty compact already, but I ran it through UglifyJS and was able to squeeze in a few more bytes. Then, comes the big guns: roadroller. That thing is pure compression magic.

With this setup, my zip ended up being 10k (more on that later).

What went wrong?

Even though I am pretty happy with the result, and I surely learned a ton, there is a lot of room for improvement:

  • Time … this was easily the hardest part for me. I ran out of time and didn’t complete everything I wanted. Part of the problem was the custom engine. This is a common pitfall if you are a coder, I knew it and still fell for it. The game is what matters, not the engine behind it.
  • I mentioned it before, but ECS could be “bettter”, more optimized, less shortcuts.
  • I wanted to create more complex behaviors for npcs … In fact, I wanted bosses and scripted enemy generation.
  • I created a very simple Tween component, but I couldn’t make it work to give the player easeIn and easeOut on the movement.
  • Sound effects and music are two big misses here. I wanted to add a synthesized mongolian drum with some patterns using zzfx and zzfxm. But I ran out of time and space.
  • When I was “done” with the code, I went for the zip, and no matter what I did, I couldn’t go below 15kb … not good enough. After spending 2 days trying to figure this out, I realized I wasn’t using the right PNG for the sprites. I had 2 versions, an optimized one and a “dev” one where i was adjusting colors, positions, etc. As soon as I chose the right image the bundled zip dropped to 10kb. Furthermore, after the jam ended, I discovered tinyPNG which cut the size of it by 50% which made the zip 8.5kb … sigh …
  • Last but not least, I would have wanted this to run on webGL. Currently it is only a canvas renderer. I’m pretty sure if I were to upgrade the ECS architecture with the fixes I mentioned above, the bottleneck will certainly be the slowness of the canvas element.

Needless to say, now that I know I had another 5kb of availability … so much could have been done.

On the bright side, a decent game with good code and non-cryptic variables can work under 13k.

  • 14 systems
  • 20 components
  • 25k or so entities
  • 60 fps
  • Behavior Trees

I’m happy. Could be better. But I’m happy.