While at The Devhouse Agency, I helped create many small 3d (and 2d) games, and for quite a few of them I was the primary systems developer. After a few projects of remaking the same basic systems again and again, I began creating all systems to be reusable, and over time it became a conglomeration of systems that I could import into projects to save days, sometimes weeks, of development time. Each of the systems listed below was created by me over the course of multiple projects.

UI (Application States)

For swift, easy, and powerful game or application states, I created something I call the Application State System. Originally intended as a UI manager, I quickly found it to be an extremely powerful tool for controlling the flow of games, and so I added additional features to it.

Application States are simple scriptable objects that represent a pre-defined application state flags enum. These scriptable objects can be activated from anywhere to change the current state of the application to what they represent. This only happens however if they are allowed to coexist with currently existing states. Additionally, these scriptable objects can toggle existing states, load scenes, toggle scenes, change the cursor lock, and pause the application, as well as any additional functionality that could be easily added. There is also a component for game objects that controls whether that object is active or not depending on the current application states. This allows the developer to easily decide what is enabled when.

Application states in action. Each button is loading a different scene, among other small details!

Objects

My implementation of a 3d game framework defines objects through inheritance. Each type of object chains its functionality into the next. These are ordered Tangible Object -> Physics Object -> Controlled Object. Made with assistance of Christian Black, who designed the original architecture before I began adapting it to further purposes.

Tangible objects contain fundamental information and functionality about an object such as health, invincibility frames, and armor frames. Tangible objects represent any generic object that could potentially be interacted with in the world. All of the types of objects contain event callbacks that allow for things to happen when objects are hit, lose health, die, etc.

Physics objects are the next step in the chain. These store physics information and references about the object, and add the ability for them to move and be knocked back. They represent objects that should use the physics system and receive knockback.

Controlled objects are the most advanced type of object. Controllers can attach to these objects and send inputs into them. Controlled objects also have an attached State Machine that handles all the actions and movement of the object using action and locomotion states. Other data stored in this type of object is: attached animators, equipment loadout, movement settings, and some miscellaneous references.

Controlled objects use the Kinematic Character Controller Asset for their movement. Additionally, all controlled objects take in normalized inputs, which can be received from a Controller. Controllers extend from a base controller class, and currently come in two variants, Player and AI.

States

States are what make controlled objects do things. The state machine that I wrote has two possible simultaneous states, one Action State, and one Locomotion State. Both types of these states extend from the same generic State, so much of their functionality is the same.

Basic States command which animation to play, for how long to remain in each state, what inputs can be used during the state, and what kind of locomotion should be possible during the state. While basic locomotion and action states can be used, the real power comes from inheriting from them. More advanced action states, like the Attack State, define things like frame timings on when hitboxes come out, when it is possible to animation cancel, what states is possible to switch into, SFX/VFX, and plenty more.

Controlled Object Locomotion via States, seen on the player and the zombies
Projectile equipment in use to kill zombie AI

Equipment

Equipment is the name I gave to ‘equipable’ actions. These are prefabs that connect to input bindings. They can do anything, but their main purpose is to set the controlled object’s action state or change their locomotion states. Some examples include jumping, crouching, and attacks such as shooting or melee attacks. Equipment use can be limited by the current state the controlled object is in. They exist as prefabs because they can also have an attached animator or model.

The equipment script can easily be extended to add more complex functionality. For example, a projectile weapon equipment could be generalized into a Limited Use Equipment, and crouch, as well as many other things could be Toggled Equipment.

AI

The AI Controller in this 3d game system is a type of Controller that sends inputs to Controlled Objects. This was my first attempt at a flexible AI system that allowed for quick implementation of new AI, so it is somewhat messy, although I have many ideas on how it could be improved. This AI has the same logical code for all AI behaviours, and the actual choice making is done through behaviour setting scriptable objects.

The AI controller decides if and which objects in the environment it should target, which attack to use against the target, and how to move in order to position itself into an opportune place for the attack. All of these behaviours are driven and manipulated by the AI Behaviour scriptable object, essentially the AI’s settings file. Attacks have ranges and cooldowns to guide the AI’s behaviour further. Specific movement types can be set to be used when certain criteria are met, and those tell the AI controller which movement preset to use. Additional movement presets can be added easily through code as well.

Projectiles

With the Projectile system I wrote, projectiles are not physically simulated, but move and check collisions on their own. Originally, I had planned to have all projectiles come from the same object pool, with their properties set on fetching, but due to technical difficulties and time constraints, that feature was abandoned. Since then, I have created a superior pooling system, seen below.

Projectile Behaviour is adjusted through the use of a scriptable object. It has numerous features that could be used for nearly any projectile idea, weather that be hitscan weapons, physical bullets, arrows, rockets, cluster bombs, sticky grenades, flamethrowers, persistent acid potions, and whatever else you could think of. Any further specific features could be added easily, all of those ideas are supported just by changing settings out of the box!

Progress Saving

Saving progress was tricky, but the solution I came up with was to track whether discrete ‘events’ had been reached or not. This gives the developer more control over where they want checkpoints to happen. A Progress Manager handles loading and saving to/from JSON format, and using a component called a Progress Object, Game Objects can be toggled on or off based on the status of specific progress trackers.

Progress Trackers are scriptable objects that store a boolean. Each of these is one of the ‘events’, and can be triggered or toggled regardless of scene. They have additional data for cascade loading in a debug situation in editor. Children of the generic progress tracker store additional data, such as Checkpoint Progress Tracker‘s note of where to spawn the player during a respawn.

A minor system parallel to progress trackers is State Progress Dictionaries. These dictionaries provide an updated object that is determined by which progress trackers are activated. They can determine which abilities are active, the most recent checkpoint, etc.

Audio

Audio in the framework is controlled via an Audio Manager. The manager pools audio sources and has two separate pools, one for music, and one for sound effects. Music/Audio Tracks have a scriptable object that allows you to define volume, whether the song should loop, and how long it takes to fade in/out. Sound Effects also have a scriptable object that does a bit more. These take in a list of audio files, and can be randomized or ordered, pitched, looped, delayed, and even what subtitles to display.

Level Loading

The level loading system loads groups of scenes asynchronously. This lets the player traverse scenes continuously, although the performance hit upon loading a new level is very dependent on the size of each of the scenes. Further optimizations could be made, but it would ultimately require many small scenes to rid of the lag spikes during level loads. Persistent objects exist in a special persistents scene that is always loaded.

Levels are defined in Level Data scriptable objects, and the Level Manager handles scene loading logic. Each scene contains a Level Controller which is in charge of handling scene change logic and ensuring the persistent scene is always loaded.

Transitions

Transitions are scripts that change objects on a global trigger, these can have callbacks for certain points during the transition. A good example of a transition is a loading screen. Upon reaching the half-way point of the transition, a level load occurs. Transitions aren’t just for UI however, they can also be used for any other type of change (usually en mass) through the application.

Object Pooling

My version of Object Pooling is one pool per-prefab. Using a reference to the prefab’s object pooling script, it can be called to create new instances or grab existing ones that have been released back into the pool automatically. If the pool size is maxed, null is returned instead.

Miscellaneous

Singletons

A basic Singleton abstract parent class, no more writing the singleton structure repeatedly for every manager under the sun!

Extensions

Various Extensions for existing Unity classes.

Credits

Zombie Game

Programming: Trevor Thacker, Christian Black, Liam Gilmore

Level Design: Daniel Shae, Liam Gilmore, Zachariah Shaikh

Art: Dylan Elkins, Zachariah Shaikh

Animations: Liam Gilmore, Devyn Cole

Lighting: Devyn Cole, Liam Gilmore

Fighting Game

UI States: Trevor Thacker

Fighting Game: Christian Black

Jump Game

Programming and Development: Trevor Thacker