Basic Enemy AI in Unity 2019.4 for 2.5D Shooter

J. V. Gagliardi
7 min readAug 30, 2021

--

Objective: Create a basic enemy for the player to attack and learn how to limit the player‘s’ range of motion.

Creating Enemy Prefab

To start creating the enemy, first create a new 3D object in the hierarchy. We will call this cube “Enemy”. Make sure you zero out the position and rotation if they are anything other than zero. The enemy’s scale can be left at one.

The Enemy and Player look almost identical. To differentiate the two, a material will be added to the enemy object. To create a material, first make a new folder called Materials for the project. Simply right-click on that folder and create a new material. Click on the new material and select the color blotch next to the Albedo option. Since enemies are supposed to be evil, adjust the color slider to be red. When this step is complete, rename the material “Enemy.”

The enemy is almost complete! To change the enemy’s color, drag and drop the newly created material onto the enemy. It should turn red if done correctly. Now drag the enemy object from the hierarchy to the prefabs folder.

The last step for now is to create a tag. This tag is going to help the player determine what it collided with.

To create a tag, select the enemy prefab and click on the tag dropdown list and select new tag. Call this tag Enemy and assign it to the enemy prefab.

Finally time for some coding!

Enemy Script

The next thing on the to-do list is to create the Enemy script. Place the script in the Scripts folder and drag it onto the Enemy prefab. Open up the script and remove the default methods.

Just like the player we created in the basic player movement tutorial, the enemy is going to need to move. So before starting, it will need some variables. One variable will be a float called _spd set to 1.0f, and the other one will be an int called _health set to 1. Both variables will need a [SerializeField] attribute.

Next create three methods for our enemy. The first will be an Update(), followed by EnemyMovement() and EnemyDamage().

The update method should check to see if the enemy’s health is greater than zero. If so, the EnemyMovement() will be called. If not, the enemy game object will be destroyed.

The enemy movement will be simplified in comparison to the player’s movement. The direction will be set to move left at the speed at which it’s moving. Then transform.Translate will be called to actually move the enemy. If a more in depth explanation is needed, check out this link to see how the player moves.

Finally, the enemy is going to need to take damage. This method will use an argument. An argument is usually a changing value that gets passed into a method. The EnemyDamage() will take an integer as it’s argument. The thought behind this may be because the enemy might take damage from multiple weapons and/or objects. The damage will be subtracted from the enemy’s health, then when it reaches zero, the enemy will destroy.

One final thing the enemy needs is way to die if it gets off the screen. This can be done by checking if the enemy’s current x position in EnemyMovement()is less than a certain value. I found -12.5 works well. The same code can be used to destroy the bullet. Just apply it to the bullet’s Update(). Makes sure the value is for the right hand side of the screen though!

Create Spawn Manager

Now that an enemy has been created, we can start having some fun…sorta. To create a level, just drag and whole bunch of enemies into the hierarchy and hit play, right? Wrong!

The easiest way to do this is to have a basic spawn manager to start spawning the enemies. The first step is to create an empty game object in the hierarchy with the name SpawnManager. Create a new C# script with the same name and drag and drop it onto the SpawnManager object.

Open up the script and remove all the default methods that come with it.

Like usual, the first thing to do is create the variables we will need to start spawning enemies. In this case the variables will be: a Vector3 called _spawnPos to generate a random spawning position; A SerializeField GameObject variable called _enemies to store the enemy that needs to spawn; A Player variable called _player to communicate with the player object; Finally a WaitforSeconds variable _spawnTimer, to help the performance of the game.

The idea of the SpawnManager is to spawn enemies at a random position while the player is alive. With this info, we can add a few methods. The first being Start(). Here the player object can be initialized and the _spawnTimer’s value can be set to two and a half seconds. The last thing to do is to check if the player object exist, if it does, then we can spawn enemies. Using StartCoroutine(SpawnEnemies()).

You might be wondering what StartCoroutine does. StartCoroutine is usually used for bits of code that might want be used in time intervals. For us, we want to spawn enemies every so many seconds.

To start using this function, create another method called IEnumerator SpawnEnemies(). The first thing to do in this coroutine is to wait to return the value of the timer. This will spawn enemies in timed intervals. Before the enemy is instantiated, a spawning position must be generated that way the position is random for each enemy. We can create another method called GenerateSpawnPos() and call it in the coroutine. Finally, we instantiate the enemy at the generated _spawnPos with no rotation.

In the GenerateSpawnPos method, a random value for the enemy’s y position needs to be generated, that way the player can’t camp the whole time…nobody likes a camper. To do that a local float variable called pos can be set to a random number in a certain range. What worked best for me was from -3.5 to 5.5.

Then _spawnPos can be set equal to a new Vector3 the pos variable is the y value.

Note: The 13.0f was derived from dragging the enemy off screen until I couldn’t see it anymore. That way the enemy spawns off screen on the right hand side

The Enemy’s OnTriggerEnter() Method

Before the last step, create another tag. The tag should be called Bullet. Assign the Bullet Tag to the bullet prefab.

A cool method to use is called OnTriggerEnter(). With this method we can check if we collided with an object of a certain tag. If we did, for now we will just destroy the enemy. Beware! This will only work on an object if the collider component’s check box for Is Trigger is checked.

In this method, we can check if the other collider’s tag is equal to Player or Bullet. If it is, we will just destroy the objects for now to show that it works. Later a more detailed damage system will be implemented.

Note: We can destroy the bullet when we collide with it

Restrict Player Movement

Restricting the player’s movement sounds difficult, but it’s actually not that bad. The task will be made easier with the function called Mathf.Clamp().

Mathf.Clamp() ensures that whatever values are passed into the function, that it clamps those values between the minimum and maximum arguments. The value that needs to be clamped is the player’s position.

Two local variables can be created for the x and y clamping. These variables and function all have to be called after the player’s translate function gets called. The reason for this is so the values for the x and y clamp have updated to the current player’s position after the player has moved. It can then check if the values have exceeded the clamping values. If they have, it will update the players position to be either the maximum or minimum value.

Unfortunately, a bug was found with the player’s movement. It occurs when the player moves both horizontally and vertically at the same time and causes the player to move faster than if only one button was pressed. So to solve this, the .normalized attribute can be added to the player’s direction vector. This attribute makes all values equal to 1.

The PlayerMovement method after updates have been added.

Congratulations!

The final result should look similar to this.

Nice work! Thanks for making it through this tutorial with me! Next up will be damaging the player and making a simple UI system.

--

--

J. V. Gagliardi
0 Followers

Just an average guy who wants to help people out.