
7/2021 - 12/2021
Tank Mania


Engine/Software
Languages
Roles
Notable Features Worked On
: Unity3D
: C#
: Lead Programmer
: Turrets System (Targeting & Tracking), ScriptableObject Stat/Upgrade System, Custom Editor
About
Tank Mania is an arena shooter in development drawing inspiration from games such as Crimsonland and Risk of Rain 2. The game concept was based on an idea I had with a tank where you could obtain and place many different kinds of AI-controlled turrets on the tank. A stretch goal would be having the turrets be controlled by other players locally. There is also an idea to have an upgrade system similar to Crimsonland's perk system.
Upgrades System
This upgrade system has since been modified to include a minor and major upgrade system. For now, only the minor upgrade system has been partially implemented. Minor upgrade systems would follow Crimsonland's perk system, but would only provide small stat changes. Major upgrades would follow the typical skill trees found in RPGs such as Diablo and Borderlands and have more significant and mechanic changing modifications. (In a sense, this is somewhat similar to Path of Exile's upgrade tree, having minor upgrades between major upgrades) But there is a caveat; whichever upgrades were not chosen would be applied with a lesser percentage to the enemy. Each minor upgrade would also contribute a certain amount of "points" to one or many major upgrade trees, if enough points are accumulated, a major upgrade can be upgraded.
ScriptableObject System & Custom Editor
Within the Unity Editor, much effort has been put into creating a customised Editor for these upgrades. As upgrades can potentially affect many different stats from many different units, a system had to be designed for this. While searching for an alternative to Singletons, I came across a very interesting talk by Ryan Hipple from Schell Games which proposed many unique usages for SingletonObjects that I had never considered before. From there, I began designing many systems around the use of SingletonObjects. Of course, the upgrades and stats systems would also benefit from this design.
Let's start with individual Stats. Each Stat is a ScriptableObject containing:
1. Action event for when the Stat gets upgraded (or when its value gets changed in any way)
2. The serializable base value. This is a float.
3. A private value after upgrades.
4. A property for returning the upgraded value. There is also another property for returning the value as an int, rounded with Mathf.
5. Whether the Stat is of int or float type.
6. The total additive and multiplicative values from upgrades.
1. is useful when the state or value of some other system relies on a Stat value. Examples of use cases for this that have been implemented are the UI for the bullets, where the UI system would want to know how many bullets the turret holds so that it can reflect that, and something as simple as changing the size of an array to hold the collider results from Physics.OverlapSphereNonAlloc for a proximity triggerer. The rest are pretty self explanatory.
To easily manage groups of Stats together, I also created a StatsGroup abstract ScriptableObject, from which different types of StatsGroup can be inherited from. This is useful because there will be instances of groups of stats that we always want bundled together. A Health StatsGroup will always, for example, have a max health and a health regeneration value.


Otherwise, the only functionality of the StatsGroup is in the Editor. One of this is to find the list of Stats objects it holds through reflection. With this information, the StatsGroup acts as a interface for the individual Stat objects it holds, and the user will not have to manually manage each Stat object. To create the Stat objects, the user first creates the StatsGroup asset they want and name it to whatever name is desired. At this point, references to each Stat object is still null. The user can just click on "Create Stat Assets" and the Stat objects will be automatically created in a folder with the same name as the StatsGroup asset. Each Stat object's base value can also be adjusted directly from here. Additionally, there are several other tools available to editing the StatsGroup references if there is a need to do so.

An Upgrade instance (also a ScriptableObject) can then easily reference individual Stat objects that will be affected by that Upgrade, as well as the amount in both additive and multiplicative values. An overview of affected Stats and the StatsGroup it belongs to is also provided, as well as a test value to see the change.
As our system means that a large amount of Stats will be affected at once, a StatsCategory ScriptableObject was also created to store Stats belonging to the same category, for example, all HP Stats belonging to Enemies. This way, that StatsCategory can be directly placed into the Upgrade instead of having to manually assign all of them to each Upgrade instance.