Enemies and Upgrade System (Week 10 Devlog)


Enemies and Upgrade System

General Overview

Welcome to another weekly devlog update of Forsaken Colony: Last Survivors! After playing with shader graphs and level blocking last week we now turn to some more exciting aspects of the game: enemies and stage progression! However, I have to dampen your enthusiasm a little because this devlog is going to focus mostly on scripting and mathematical implementations of these elements; This is to get a better understanding how all these systems work in symbiosis in the background of my game.

Upgrade System

Stats

For the sake of making stats of different enemies and the player more comparable I decided to introduce a Stats-class which has derived classes for all types of objects with stats. There is a class PlayerStats for stats of the player and enemies, AttackStats and ProjectileAttackStats for attacks of the player (as ProjectileAttack itself is a derived class from AttackBaseClass it has some additional stats that can only be found in this type of attack). The Stats-class itself is derived from ScriptableObject which allows easy creation of Stats-assets for all characters in the game.

For the damage calculations of enemies and the player I added a static method to the PlayerStats class which takes two PlayerStats class instances as arguments (receiverStats and dealerStats) and calculates the damage dealt based on various stats; Stats effecting the calculation are physicalDamage, armorPenetrationFlat, armorPenetrationPercentage (and the same for magicDamage) of the dealerStats and armor/magicResistance of the receiverStats. The code snippet showing the static method can be seen in Figure 1, the corresponding function graph in Figure 2. There is another function which does essentially the same but takes an additional AttackStats argument and uses parameters from this class as well.

Figure 1: Code for DamageCalculation function

Figure 2: Graph of damage reduction depending on armor/magic resistance values

StatsIncrease

To generalise the way upgrades work and how they are applied to classes derived from the Stats class I decided to introduce a StatsIncrease class. This class stores values for absolute and relative increases of every stat in the Stats class. For instance, for physicalDamage in the PlayerStats class there are values flatPhysicalDamage and percPhysicalDamage in the PlayerStatsIncrease class; To apply these value changes to the individual Stats class instance I decided to override the plus operator for adding a StatsIncrease-object to a Stats-object. The formula how these values are generally applied is:

newStat = (oldStat + flatStatIncrease) * (1 + percStatIncrease)

(Note: some stats are integer values, for those only a flatStatIncrease exists)

Furthermore, I implemented the functionality to add two StatsIncrease objects. Therefore, the PlayerBaseClass only has to store one StatsIncrease object that is changed every time an upgrade is applied.

Levelling Up

Upon levelling up there are always four different upgrade options to choose from (Figure 3). Currently, the upgrades come in three different rarities and are chosen randomly when reaching a new level. Every level the amount of experience required for the next level up increases. I first used a polynomial function to determine but the progression in the early stage of the game was very slow (amount of experience required increased too fast) and once you passed a certain time in the stage you got a new level up every other second. Therefore, I decided it to change it to an exponential function with 10% growth every level:

xpNextLevel = xpFirstLevel * (1.1 ^ currLevel)


Figure 3: Select upgrade on level up (unfinished)

Enemy Types

There are currently two enemy types in the game which are ranged and melee enemies. Both of these use the move functionality from the tutorial and just directly move into the direction of the player. All enemies deal damage on contact (there is a small amount of invincibility time after the player is hit by an enemy to avoid being killed in a fraction of a second); Ranged enemies stop when the player is within 60% percent of their attack range and start a casting animation, which fires a projectile into the direction of the player. The first stage of the game currently features three different melee enemies (goblin, golem and bronze golem) and two different ranged enemies (green lizard and white lizard) which can be seen in Figure 4. All of them have different stats with enemies showing up later in the stage generally being stronger and having better stats. Figure 5 shows different enemies in a later part of the stage.

Figure 4: Dying animations of enemies

Figure 5: Current mid- to endgame

Spawning System

The general idea for spawning of enemies is that they should spawn increasingly fast throughout the stage and that there are different enemy types during different parts of the stage. Therefore, I implemented a serializable struct which allows the change of spawning parameters in the editor without having to change anything in a script.

Spawning Sequence

This struct features a field with a prefab of the enemy to spawn and an array of another struct; The second struct has fields for the time where this enemy starts and stops spawning, the time it takes for the spawnrate to double and how many instances of this enemy can be alive at the same time. Figure 6 shows what defining the parameters in the editor looks like.

Figure 6: Spawning detail of goblin enemy

The formula for calculating the current spawnrate consists of two parts: the first part features the exponential function which doubles the spawnrate after the specified time; The second part decreases the spawnrate depending on the number of enemies of the specified type currently alive. If this number equals the number of max enemies alive the spawnrate is decreased to 60% of what it would be; The complete formula is:

spawnrate = startingSpawnrate * (2^((timeSinceLevelLoad - startSpawnTime) / doubleSpawnrateTime) * (1 - 100^(currEnemiesAlive / maxEnemiesAlive) - 1.2)

The corresponding graphs for the two parts of the formula can be seen in Figure 7.

Figure 7: Spawnrate function graphs

Player Feedback and Planned Improvements

Main points of criticism this week were:

  • First enemy type (goblin) takes too many hits to kill -> tedious early game progression
  • Experience orbs are hard to collect because the collection radius is too small
  • A bug where ranged enemies seem to teleport after casting
  • A bug where enemies are sliding across the screen with very high movement speed

Planned improvements on existing features until the next update are:

  • Decrease the health of the goblin enemy to make the early game more fun
  • Add upgrades for the XP collection radius to the upgrade pool
  • Implement different background colors for different upgrade rarities, implement icons for all available upgrades
  • Figure out what causes the bugs mentioned above and fix them

Get Forsaken Colony: Last Survivors

Leave a comment

Log in with itch.io to leave a comment.