Let's talk about complex systems


Salut a tous, hey everyone. Today I fancied writing a little post about systems, things like why they're useful, when I remove them, why I remove them etc. I will try cover every system in my game which I feel is complex or useful.

When I say "system" in this article, what I'm talking about is a bit of code that "does something" but more than just a simple function. It's a little program in itself that could be taken out of the game and used somewhere else. We call them "libraries" "packages" and "dependencies" in the programming world.

Library Systems:

  • PixiJS - WebGL rendering (incl. sprite drawing, events, filters, particles & spine)
  • EasyStar JS - Simple A* Path finding, with some custom changes
  • Howler JS - Audio provides positional audio, panning etc
  • Event Emitter - Event Emitter, e.g. on('mousedown')
  • MatterJS - Physics for bullets and AI (probably overkill)
  • TweenJS - Tweens, for animations
  • SimplexNoise - Noise generation used in map creation
  • ZeptoJS - General DOM / JS utilities, Used in tooling

Custom Systems:

Indexing System:

I'm sure most games will have these, some form of hash list or black book containing all the games objects. They're great for all sorts of things. If you need to think about your performance, managing lots of objects and how those objects clean up after themselves then building a robust indexing system will absolutely aid in your quest for performance. It's a trade off between the CPU and Memory, storing many lists which eat memory - but those lists are much shorter indexed versions of any other existing list, and therefore much faster to read from.

Lets take this example, I have 100 objects on the game, some trees, some players and some bullets.

There are 50 trees and the rest are made up of other objects. If I need to do something with all the players, it's better to loop through a list of exactly "just" the players, and not ALL the objects in the scene, checking which one is a player. Therefore with a good indexing system I can do something like this:

tress = index.getIndex('trees');

players = index.getIndex('players);

Further more, if you want just all the red trees, then when a tree is created, store it in both indexes, the tree index and the red-tree index. If your indexing system is robust, when the tree is deleted then it is also removed from both lists.

AI Weight System:

This system has been rewritten 3 times now since my first game Arcade Builder. So originally AI just did "jobs" blindly, go here do this, if you need toilet, goto toilet. Simple.

The latest version of this uses a list of possible "things to do" and then weights associated with those "things".

For example this is how "heal yourself" might look:

If(health>50) {

return 0; //Don't need to heal

} else {

return 50; //I should heal

}

But now if we have another "thing to do" with a higher return value, it will skip healing and do that other task.

Consider if the logic below was checking if the "thing to do" is "attack enemy"

if(beingAttack) {

return 51;

} else {

return 0;

}

Since it has a higher return value, the AI will ensure it attacks until it isn't being attacked, AND THEN heal itself.

GUI & Components

I have a dream where one day native apps running JS without a DOM exist (I know, crazy). Because of this I do not use any frontend libraries or frameworks. The reason I do this is because if one day my app can be packaged with just webGL/canvas and the Javascript, then that might have a lot of advantages.

Because I'm holding out for a domless experience, I decided to undertake the unreasonable challenge of writing all the UI's in the renderer PixiJS.

Due to the complexity of creating UI's in raw Javascript, I created a DRY component system where, for example, you can create the code for a button, and then re-use that button everywhere.


Grid Chunking

This system essentially takes 10x10 chunks of the map and makes them saveable, loadable and destroyable. This allows the map to be as big as I want (within reason). Right now the grid chuking handles 100x100 total chunks (10,000 chunks).


Systems that were implemented then not used or removed

Quadtree

I spent quite a lot of time doing quadtree implementations, testing, etc. In the end given my system covers off so many other aspects of "how to get an item" I found quadtree wasn't necessary; heres why.

When I search for an item in my game, I don't do it in 1 tick (1 frame). I spread out the search over multiple ticks. This means the game never halts, I get my item, and nothing looks weird.

If I introduce quadtree and ignore my indexes as well as my "trickling" search approach I actually see a performance decrease, since we're now taking advantage of the quad tree and searching the tree in one frame for the item we want.

I'm sure if I *started* with a quad tree, my approach to alot of things would have changed, and I would see some benefits; but because I already created several systems to "search" for things, implementing quad tree would just be an exercise in practising coding. But we'll see. I'm not shy to completely rewrite a system from scratch.

Thread based Disk loading/saving

So the game loads/saves files from the disk. Sometimes these files are huge to transmit on the main thread, and so I created a system to send files to a worker thread first, then save it to disk (thus freeing up the main thread).

I had it working and tested all good, but after doing more work with chunking and saving chunks to memory in a list, I had no requirement to use this system. Oh welp! If you ever need to save a huge file on main thread and have issues speak to me! xD

HTML Based GUI's

This was a few years ago but I developed essentially a react-like system. I instantly saw why React exists; it solves so many of the issues you will run into when developing responsive DOM apps. But I don't want react in my game. It's a big dependency and doesn't help me get towards a "domless" experience. After writing a few days worth of systems and trying it out; I saw it was a no-go. Thats when I bit the bullet and wrote a component system for the renderer I use.

Systems that have been replaced over time

Qiao Path Finding 

This engine is actually really good and boasts a lot of features although it's performance and accuracy can be questionable at times. Also given it's vast complexity I saw it as a bit overkill for what I needed. I had always ran easystarjs and qiao in the engine, even now both hot swappable - but I have made core changes to easystarjs, since it's a lot easier to maintain.

Pixi-Tilemap 

Although pixi-tilemap is a great library which fulfills my requirements (aswell as being the system used in Arcade Builder); I lack the experience of understanding the more low level aspects of the library (mostly, playing ignorant and lazy). Since it was important to load chunks exactly how I wanted, and to be able to troubleshoot any issues, I decided to rewrite this into a much more basic Graphics object. After finding the performance impact on desktop to be little, I decided to keep the rewritten system. Although Pixi-tilemap still works with the engine and can be used at any time, which might be best for things like mobile.

Get Dead Desert

Leave a comment

Log in with itch.io to leave a comment.