Unity Beginner to Hero Part 14: Enemy Units!

In a previous lesson, we added some functionality to allow objects to be damaged by the rockets we fired. However, the objects we were shooting at were static boxes; not the most exciting foes out there. In this tutorial we will add in an enemy that will follow the player around the level.

Let’s start off by creating another capsule in our scene. Name it “Enemy”, and add a CharacterController component to it. Like we did with our Player earlier, create a Box and parent it to the new Enemy to act as it’s nose. (Since our Enemy will turn to chase the player, it will be important to know which direction he’s facing.) Finally, make sure you delete the Collider components off the Capsule and the Box to ensure our Enemy does not collide with himself. Finally, let’s do something new before we move on to giving our Enemy some logic.

Over in the project panel, right click and select Create->Material. A new material will appear among your assets, indicated by a little blue sphere icon next to it. Name it “Enemy”. Materials are used to define how objects in the game world are rendered. Since we haven’t created any materials before, all the objects in our world use the default Unity material, which is white. Materials define all sorts of behaviour, such as transparency, reflection, color, texture, luminosity—the list goes on. For now, we’ll simply modify this materials color to better display the enemy.

Click on the material. Under Main Maps you’ll see a white color swatch next to Albedo. Select it and choose a color (I picked a saturated red). Once you’re done this, drag the material from the project view onto the capsule and box objects that make up your enemy to turn them red.


Let’s move on to some scripting. Create a new C# script and name it “EnemyMovement”. The job of this script will be to rotate the Enemy towards the player and move in her direction. We can define some public fields with that in mind.

public float speed = 2.0f;
public float turnSpeed = 180.0f;

private CharacterController controller;
private Transform player;

We’ve also added a couple private fields. One is to store the attached CharacterController component, and the other will be used to store a reference to the player’s Transform. We’ll talk about how to grab the player’s Transform shortly, but for now let’s write some code to move our enemy around.

void Start()
   controller = GetComponent<CharacterController>();

void Update () 
   Vector3 direction = player.position - transform.position;
   direction.y = 0;

   transform.rotation = Quaternion.LookRotation(direction);

   controller.Move(transform.forward * speed * Time.deltaTime);

The first thing we do in update is get the direction between the player and our enemy. To get the direction between any two points in space, you subtract the origin from the target, which in our case is the enemy and the player, respectively. You can draw out some points on a piece of paper to test this.

Subtracting B-A gives us the vector in red, which is (3,3). This vector can be reduced down to (1,1), or perfectly diagonal.

Subtracting B−A gives us the vector in red, which is (3,3). This vector can be reduced down to (1,1), or perfectly diagonal.

We then set the amount in our vector to zero, since we don’t want our care if how much the player is above or below us: we just want to know the direction on the ZX plane. Once we have the direction we call upon our old friend Quaternion.LookRotation (explained in Lesson 9) to rotate the enemy in the proper direction. Lastly, we call CharacterController.Move to translate the enemy in his forward direction (which currently should be facing the player), multiplied by speed, and then multiplied by deltaTime to make it framerate independent.

This is all well and good, but astute readers have likely spotted a problem by now: how do we assign the player’s Transform into our player variable? To do this, go back in to the Editor and select our character. At the top of the Inspector (just below her name), you’ll see a dropdown titled “Tag”, with the current value being Untagged. Change that to Player.

Back in our script, add the following line to the Start method.

player = GameObject.FindWithTag("Player").transform;

Baller. We can now run the project and our enemy will track the player, moving slowly towards her. This is pretty neat, but right now the enemy turns towards the player way too fast. You can see this by jumping over the enemy: he’ll just snap instantly towards you. Let’s fix this.

Instead of having our rotation be immediately set to the direction between the enemy and the player, let’s instead take our current direction and smoothly move towards our target direction. Modify the rotation line to be the following:

transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(direction), turnSpeed * Time.deltaTime);

We’re using a new method here, Quaternion.RotateTowards. This method takes in two rotations: a “current” and a “target”, as well as a float that defines how far to travel between the two. Save and run the project, and you’ll see the enemy now takes about two seconds to fully rotate towards the player (when pointing in the opposite direction).

The suspense.

The suspense.

As a final touch before we close out this lesson, add the EnemyHealth script to the enemy to allow us to destroy him with our rockets. In the next lesson, we’ll add in some code to give the enemy the ability to damage and destroy the player.

Lesson 14 Complete Project

Thanks for reading! If you have any problems with the above tutorial, general comments or suggestions, please post below. It’s always appreciated!

14 thoughts on “Unity Beginner to Hero Part 14: Enemy Units!

  1. this whole tutorial was perfect! i learnt so much more from this than just watching videos and what not. it would be amazing if you were to continue on this. maybe getting animations to work smoothly and have the player start to move and stop moving smoothly instead of just being so sudden! keep up the good work! 🙂

    • Right now I’m currently building a new website, written from scratch instead of wordpress, but once that’s done I definitely plan to continue the series. Like you said, showing how to import meshes/animations is key, along with good techniques to tweak character movement acceleration/decceleration (something that is missing from so many Unity tutorials!).

      In any case glad the series worked out for you!

  2. This is a great set of tutorials. Highly anticipating more, your style and flow of teaching are perfect for me. Thank you.

  3. Hi Eric,
    Thank you for these great series, especially those related to custom character controller, I like it a lot.
    BTW have you ever met a problem with Grounded method check when using official CharacterController component? Basically jumping is not reliable. Character sometimes refuses to jump. I’m running the code in Update method but it behaves like if the GetButtonDown was checked in FixedUpdate ;).
    I’m facing really strange problem which I described here:
    Looks like the more faster the game runs, the less I can jump.
    Even just putting Debug.Log to the Move method solves the issue…

    • As far as I recall, IsGrounded is set to true if the controller’s bottom collision flag is set to true. I.e., if you collide with something below you. It’s possible that the controller is colliding in one frame and being pushed back far enough that it doesn’t collide the next frame (at very high framerates) due to the character’s skin width, but I don’t use the built in controller much anymore (outside of these tutorials) so I wouldn’t be an expert.

      Are you having an issue with the CC in general, or in these tutorials? I opened the latest tutorial project and jumping is consistent in it at ~1500fps.

  4. I found out it never happens when VSync is on or when putting the extra print / debug.log line to the move method. I’ve reported a bug.
    It can also be reproduced in your project in case V Sync is set to off (don’t sync).

  5. This is not a bug of Unity. The problem occurs on high frame rates because I have set “Min Move Distance” to 0.001 and called _characterController.Move() method with velocity that was multiplied by Time.deltaTime which will give very small value on high frame rates (smaller then 0.001). When character is not moving isGrounded returns false.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s