The time has come to create some basic enemies for the FPS prototype. The implementation includes basic AI behaviour, field of view and mobs being able to kill the player.
Mob prefabs are simple capsules with a HealthManager and a BasicAi script attached to them. BasicAi makes the enemies move and also casts a sphere in every update to check if an obstacle is blocking the way of the mob. If an obstacle is hit by the ray and is within the distance set in obstacleRange then a random angle is chosen within [-110, 110] which becomes the new orientation for the mob. This simple logic stops enemies from bumping into or going through obstacles.
Lesson learned: always check the number of arguments when using a layermask with raycast. I spent more than an hour debugging the code because the layermask didn’t seem to work at all only to find out that the layermask was implicitly cast to a float and was considered as a distance value by the raycast method :D.
BasicAi also has a ShootTargets() coroutine. This iterates over all the visible targets registered by the FOV script, instantiates a bullet prefab and rotates it towards the target. My decision to move the center of the player’s character controller to its feet now proved to be problematic because using LookAt() on all axes made the bullet fly towards the feet and colliding with the floor. At the moment, the bullet’s y position remains intact so the bullet does not change its vertical angle. The coroutine yields for attackCooldown seconds before firing again.
Bullet also has its own script. In Update() it moves the bullet with the given speed, if something else enters its trigger collider the bullet is destroyed (except when the other object is also a projectile). A reference is acquired to the target’s HealthManager (if it has one) and its Damage() method is invoked with the bullet’s damage.
I won’t include the FOV script here, it is mostly based on Sebastian Lague’s tutorial and also has an editor extension making field of view circles visible when an enemy is selected. Of course, the code can be found on my BitBucket as always.
To make enemies be able to hurt the player I made separate implementations of NonPlayerHealthManager and PlayerHealthManager. These two scripts both inherit from the abstract class HealthManager. Upon reaching a health amount of 0 or less, PlayerHealthManager disables all control scripts, unlocks the mouse cursor and calls the method OnGameOver() which has been implemented in a new script called MainLogic attached to a GameManager object.
This method enables a special game over GUI panel and a restart button which reloads the current scene. This coupling between the manager class and PlayerHealthManager is quite ugly so I’m going to add some messaging system to the code next time.