GameDevHQ — Day 24 — NovaStar Dev Diary: Scripted Enemy Behavior
Today we continued development on our NovaStar project and with the weapons locked in we put our focus onto developing our enemy roster for the game. My responsibility for the day was to build a new enemy type that is small, fast, and dies in one hit which we can call the “speed cruiser”. This type of enemy is meant to be fodder that harasses the player and can be used in larger numbers to give the piercing shot types greater utility. Their fast speed and smaller size makes them difficult targets to hit but their fragility means that the later weapons with larger shot sizes are more effective at dealing with them. This gives them a power curve that correlates to the player’s progress where they are a larger threat at lower power levels since the player is just as fragile and the small size of the basic shot type makes these speed cruiser enemies difficult targets to hit. As the player increases in power the larger shots trivialize targeting these enemies but the way our health system works where the player can be downgraded to lower weapon tiers when receiving damage means that these enemies still retain a level of threat despite being easily dispatched.
Developing the enemy behavior
My design for this enemy involved giving it two major behavior patterns: one that involved a consistent movement pattern and the other involved a stop and start movement pattern. The first pattern was straightforward in that the enemy will spawn and immediately fly in a line at a constant speed and fires a a single shot shortly after each time it enters the screen. This version of the movement pattern causes the enemy to act as a constant threat as it loops back around to the right side of the screen each time it travels off of screen left. The second pattern is a bit more complex on the logic end but the goal of the behavior is for the enemy to quickly enter the scene, stop, fire a shot, then travel forward at a faster pace than it did when it entered the screen. This allows for the enemy to be more of a telegraphed harasser where the period it takes to pause its movement gives the player a warning of the incoming shot and charge trajectory while also providing them with an easier window to shoot down the enemy.
I wanted to attempt to make either of these behaviors easily managed within a single script which means that a few boolean switches were incorporated to allows us to toggle the different behaviors that the enemy could be capable of. The two major behaviors that were incorporated were the ability to toggle between the two major movement patterns, whether or not the enemy randomizes its position each time it resets to the right side of the screen, and whether or not the enemy will fire shots or purely attempt to damage the player by flying into them. The backbone of this structure was an abstract class script that we made as a team to outline the general variables and functions most of the enemy types would need. This allowed each of us who went to work on developing new enemies to have a uniform baseline to work off of and make new scripts that were tailored for each of enemy designs that inherited all of its default contents from the abstract script we made. Some of the base functionality that was outlined in the abstract script was functions to control enemy movement, weapon firing, and relevant damage calculation functions.
The logic for the stop-and-start movement pattern relied primarily of coroutines for the movement controls. How it works is when the enemy is first instantiated it will enter the screen at a speed of 40 and then run a coroutine that will disable all movement after a random delay between 0.3 and 0.5 seconds. The reason for the random delay is to provide some slight variance to the enemy positioning and timings to vary up what spaces on the screen and actions the player can take count as safe. Once the enemy stops a second coroutine begins that activates a short 0.3 second delay before the enemy fires a single laser shot and then another delay to hold the enemy in place for 1 second before reengaging the enemy movement and setting their speed to 90 as they fly forward in a straight line. Once the enemy leaves the bounds of the game screen it has its speed reset and all currently running coroutines are stopped. Following this the random stop time delay is generated and the StopMovement() coroutine is run again to restart the entire cycle.
Filling out extra enemy details
Following some testing a few quick adjustments were made to account for certain situations that may be unintended or detract from the game in some way as well as polishing the presentation. The primary change that was made was providing the enemy a window of invincibility until it actually enters the screen. To do this we simply find the x axis values for both left and right ends of the screen and then set an if statement that only activates when the enemy is within those bounds. This means that if the player happens to shoot the enemy while it is off screen the hit will be registered but no damage calculations will take place. The reason for this limitation was to prevent blind spamming by the player destroying enemies before they even enter the screen and perform whatever actions they were designed to do. This should allow the enemies a bit more of a chance to attack and encourages the player to be more patient in their shots.
For presentation I also went through and allowed the enemy to instantiate an explosion animation when as it is destroyed. This required removing the delay we had implemented in the Destroy() command in the enemy and adding a new script that contains a single Destroy() command to the explosion prefab to allow us to remove the enemy from the scene the instant it dies and to also remove the animation once it finishes playing. Going for this method over destroying the enemy with a delay and replacing its visuals with the explosion will solve some potential problems down the line with certain object detection techniques such as the homing shot which previously had problems with detecting enemies that were killed by the player but not deleted from the scene due to their destroy delay.
The final addition that we needed to add was to detect player and enemy collisions and to apply damage to both parties involved. This involved detecting collisions with any objects tagged as “Player” and activating the Damage() functions on both objects. This feature needs some further development to account for enemies that have more than one health but for this initial version of the script it will do and we will have to discuss it as a team tomorrow.
This is current state of the speed cruiser enemy so far and our deadline for hitting minimum viable product is coming up fast so we are expecting to bring all of our parts together tomorrow and ensuring we have a complete and stable build. If we can achieve this by the end of the day we will have succeeded and can possibly look at expanding the features of the game before launching on itch.io. My other teammates are also hard at work at refining the other systems of the game such as the ui, wave spawning system, and checkpointing and I look forward to seeing how it all continues to come together. Until then, mahalo for reading and aloha!