Day 134 — Setting up Enemy AI
Hey and welcome!
In this one we’re going to be looking into giving our enemies some AI logic so that they can chase down our player. You’ll see some similarities here with our enemy implementations that we’ve done in the past but we’re going to be doing things a little different this time.
Start things off by adding in a cube to your game and scale it up to 3 on the y value so that it’s roughly the same height as the player. Rename this object to “Enemy” and add a colour to it by creating a new material if you wish.
Now here’s where we’re going to do something a bit different, go ahead and add a Character Controller component to your Enemy and adjust it so that it fits the enemy if it doesn’t already. We’re going to be controlling this in order to move our enemy around, similarly to what we do for the Player! Next create an EnemyAI script to attach to your enemy and then prefab this object.
Once you’ve done that it’s time to start adding in some code to our newly created script:
private CharacterController _controller;
private Transform _player;
[SerializeField]
private float _speed = 2.5f;
[SerializeField]
private float _gravity = 2.0f;private Vector3 _velocity;void Start()
{
_controller = GetComponent<CharacterController>();
_player = GameObject.FindGameObjectWithTag("Player").transform;
}void Update()
{
if (_controller.isGrounded == true)
{
Vector3 direction = _player.position - transform.position;
direction.Normalize();
direction.y = 0;
transform.localRotation = Quaternion.LookRotation(direction);
_velocity = direction * _speed;
} _velocity.y -= _gravity;
_controller.Move(_velocity * Time.deltaTime);
}
There’s a couple of new things we’re trying out here but as you can see here, similarly to our Player we’re starting things off by grabbing a reference to the Character Controller component so that we can move the enemy based on the _velocity we define.
What is a bit new though is how we’re handling our isGrounded code as we’re now specifying that the direction is the player’s position subtracted by our enemy’s position. To get a better idea of this, let’s say that our player’s transform values are (1, 0, 0) and the enemy’s values are (3, 0, 0). After this line of code, direction would be assigned the Vector3 value of (-2, 0, 0) which means that its direction is towards the player.
Since this runs each frame this check will continually happen until the enemy matches the player’s position. Now if we went ahead and multiplied this by our speed in order to get our velocity we would get (-5, 0, 0) which means that our enemy will be moving like greased lightning which is not what we’re after right now.
We need to find a way to keep the behaviour reasonable and not too snappy which is where Normalize() comes in. This normalises our Vector3 by restricting it’s length/magnitude to 1 which means that our (-2, 0, 0) vector becomes (-1, 0, 0) and when we multiply that by our speed we get a less snappy response than before. So even though it reduces the length of the vector it keeps the direction in place!
Lastly while we use the direction to get our velocity we also use it with the LookRotation() method in order to make the enemy face the player. Make sure that your player object has the tag of “Player” and when you run the game you should get this working for you:
Haven’t had one of these text heavy explanations of the code in a while but it’s important to really nail down your understanding of vectors and how the code works!