GameDevHQ — Day 21 — NovaStar Dev Diary: Power Weapons
Today we are continuing with the point of development that I left off at yesterday which was developing a new type of weapon for the player to use. The weapon in question was a sustained laser that the player can sweep over multiple enemies. Programming this mechanic was a fun challenge as it required a couple revisions using different methods with the end result being a mix of a few. Before we break down the process I would like to outline the main functions of this feature.
Parts of the feature
The laser has three different states idle, charging, and active. The idle state is when the laser is either disabled due to the player not progressing high enough up the upgrade tree to access it or the weapon is enabled but has not received the input that activates the weapon. Upon receiving an activation input, in this case the desired input is the player holding down the space key, the laser will enter its charging state. In the charging state a graphic effect plays to let the player know the beam is charging but the laser does not yet fire.
During this charging state the script will track how long the player holds down the button to a maximum of three seconds. The recorded time will be used to determine how long the laser will fire for once it enters the active state once the player releases the space key.
Upon entering the activation state the charging graphic will be removed and the laser will be fired and stay on the screen for as long as the player held the button for a maximum of three seconds and a minimum of 0.75 seconds, although this minimum is still being tweaked. If the player holds the key down for longer than three seconds the laser will still stay in the charging state until they release the key but the duration that the laser fires for will only be three seconds. If the player releases the key before holding it for the minimum charge time the charge is cancelled and the laser does not fire.
While the laser is active any enemy receives multiple instances of damage over time as long as they are colliding with the beam. This makes the laser one of the stronger weapons in the game as it allows the player to deal high damage to multiple targets.
Developing the Scripting Logic
The other weapon types we have implemented were simple in their execution as they mainly consisted of a straight forward instantiation command that created a prefab copy that would travel operate on its own internal logic, meaning their design was much more fire and forget. The charge laser mechanic was a significantly different design as most of the logic is done on the side script that fires the laser rather than on the laser itself. In it’s current design the laser object has no scripting attached to it and instead of being instantiated is an object that that is activated and deactivated.
The challenge to the design was finding a way to properly track how long the player holds down the fire key and convert that into a useable value for the fire duration. Initial designs tried to relegate all of these mechanics to a single function that was called within the Update() function however testing revealed that this had an issue with the while loop that was used to calculate time the button was held down being within a function that activated on a key being pressed down. This version of the design resulted in Unity crashing due to the while loop running infinitely as the exit condition was never met. The structure of this problematic loop was a boolean set as the loop condition that would switch to false when the difference between the starting time of the charge and the current time reached three seconds or greater. This method was flawed and the next idea for implementation was to convert the method into a coroutine to better handle constant time tracking. The issue with this attempt was that relegating all button input detection to the coroutine meant that coroutine had to be constantly running which lead to more unintentional interactions. Part of the problem was in my method I was running the coroutine in the Update() function which led to multiple instances of the coroutine being run every frame. This led to a third revision that met the needs of the mechanics I wanted and required a combination of input checks within Update(), basic functions, and coroutines.
The current version of this mechanic’s design searches for a key press and key release within Update(). Once it detects a button being pressed down it will activate the charging animation and record the time that the key was pressed. Once the key is released it will then disable the charge animation and find the difference between the current time and the start time, if the difference is greater than three seconds it will record the total charge time as three seconds instead. Once the time is recorded it will then run the FireLaser() function to handle activating the actual laser.
The FireLaser() function will activate the laser sprite, control its starting and active animations, then run a coroutine to handle the process of the laser’s firing duration and deactivation. The coroutine LaserTimer() will then use Time.deltaTime to count down from the saved total charge time value to keep the laser active until the timer runs out. Once the timer completes the ending animation should be played for the laser and then the object is deactivated again. At the time of this writing there is some work that needs to be done to properly allow the ending animation to completely play out as the current iteration deactivates the laser before the animation completes.
With the laser properly firing the final component that needed to be included was a way for the laser to properly deal damage based on a periodic interval. This limitation needs to be set as the method to detect continuous collisions is OnTriggerStay() which checks for collisions every frame, meaning that without a set delay the laser would deal an instance of damage to the target on every frame which is too much for our practical use. In the current version of the project this delay is handled on the enemy end where a short coroutine enables a window of invincibility every time the enemy receives a hit from a weapon tagged as a “Beam”, the invincibility is then disabled after 0.3 seconds which allows the enemy to hit again. This feature was the last part of the scripting that was written for the day so I want to go back and see if there is a better method of implementation. The result is the ability to control the rate of damage the laser can deal to enemies however when revisiting this design tomorrow I want to figure out a better way to keep this hit rate on the laser rather than the enemy to allow for easier editing of the hit rate as needed.
I plan to continue development tomorrow and hopefully get this all wrapped up and implemented into our weapon selection system before the weekend. For now here’s a quick demo of the current state of the feature.
Mahalo as always for reading, I’ll be checking back in tomorrow with hopefully some more substantial progress for our project. Aloha!