Challenge: Enemies That Can Dodge Projectiles

My objective for this article is to create an enemy type that can dodge the player’s attack projectile.

To get started, I will need an enemy who is ready to dodge some incoming traffic. It’s a good thing I recently made a Red Piranha that will be suitable for this new task. More on the creation of the Red Piranha here.

While there are different ways to approach this problem, and I wanted to take a path that is CPU friendly. To be honest, I have completed this challenge before. I previously had the dodging enemy checking the distance between itself and incoming projectiles in update. If the current distance between the player bullet and the enemy was less than a specified dodging distance, the enemy would attempt to dodge the projectile. This time around I wanted to see if I could take another approach that doesn’t involve constantly checking the positions of all enemy projectiles.

I decided to attach a CircleCollider2D to my Red Piranha prefab, to use as a detection zone for dodging. This way I can avoid checking for projectiles in update and simply handle them as they enter the collider. Multiple colliders on a single object can cause some unexpected behavior regarding what happens in a single script OnTriggerEnter. Without going into detail about the potential options for that route, I chose to instead create an empty object to hold the CircleCollider2D, and made that a parent of the Red Piranha prefab.

In addition to the CircleCollider2D, this parent object will get it’s own new script called Enemy Avoid Shot.

I am going to want to instantiate the new Parent object with the Red Piranha attached as a child, so I make a new prefab out of the whole thing.

One object in a collision absolutely needs to have a rigidbody. I add one to this parent object rather than the player projectile prefab.

My Spawn Manager now needs to have the Red Piranha prefab overwritten with the new parent prefab in the inspector. Now the parent object will spawn holding the Red Piranha, rather than just the Red Piranha spawning by itself.

On to the scripting, which will require a few changes. Being that the parent object is what controls the position of the child, I need to relocate my enemy movement for the Red Piranha from the shared Enemy Script to the new Enemy Avoid Shot Script. Let’s start with the changes on the Enemy script. This boundaries method is shared by all enemy types excluding the jellyfish. The first section respawns enemies on the right side of the screen as they leave the left side of the screen. These enemies don’t actually destroy as they die. They have an on death behavior that involves the enemy falling off the bottom of the screen, but the actual game object doesn’t actually destroy itself until it visually leaves the screen at the bottom. The other two areas check positions and conditions to finally destroy the enemy as they leave the screen in on death mode. If the enemyID is 2 (Red Piranha), there is a command to destroy the parent of the game object, so there aren’t a bunch of parent colliders accumulating in the scene view.

This enemy on death method gets called when the player projectile collides with the enemies. If the enemyID is the Red Piranha (2), then the argument can be entered. First I declare a local variable to store the collider of the parent object. After using GetComponent to access the parent collider, I destroy it…heartless…I know. There can be considerable time between the enemy being struck by a bullet and it leaving the screen to the bottom, so I destroy the collider to remove any unexpected behavior as the dead enemy leaves the game view.

Now for the new enemy avoid shot script that is attached to the red piranha parent game object. I need the same speed and respawn range variables as were being used for this enemy type on the enemy script. The movement is now applied here, being that the parent object will be moving the child Red Piranha prefab. I call another boundaries method on this script to handle the respawning on the right side of the screen. Because the parent is now controlling the position of the child, the enemy script won’t respawn the Red Piranha prefab on the right otherwise.

The avoid enemy shot script uses an OnTriggerStay2D method. This enables me to perform an action like in update, for as long as the collider has something inside it setting it off. I check for the tag to be Tusk, being I only want to avoid the player’s Tusk projectile. If the position of the tusk is higher than the position of the enemy, it will move down to dodge, and if the position if the tusk is lower or equal to the enemies vertical position, it will dodge up.

Each move up and down method offers exactly that. Being that the enemy is moving left via the enemy movement method, either one of these being added will result in a diagonal movement for as long as the projectile is in range of the collider.

Here are some GIFs to show the new dodging ability of the enemy! I tried to make the range so that if the player’s shot is fairly centered on the target, it will still hit. If the shot is off center, the enemy will most likely be able to dodge the projectile!

I am an artist and musician, that is currently diving headfirst into game development with C# and Unity3D.