Field of View on Enemies

Posted 2/12/2024
by TheHocken

post title image
Player detection solely based on distance felt cheap and kind of boring. To make that more interesting and give my players some gameplay options, I decided to add vision cones to my enemy units. Here is how I went about that including some of the challenges along the way!

My goals here are to allow the player to:

  • - Make use of the terrain to avoid detection
  • - Take advantage of patrolling enemies' routes
  • - Get caught in a bad spot if they aren't careful

To do this, I need a vision system that will be blocked by walls and other obstacles and trigger enemy pursuit if the player is spotted. I saw examples of using colliders and meshes built from raycasts, but flat colliders didn't account for obstructions and meshes seemed like overkill and probably too resource-intensive for my game. So, I decided to just use raycasts to implement. Raycasts should be able to meet my needs and be relatively lightweight.

I started by placing the start point of my rays at the center of the enemy unit and then cast them out in a cone shape looking for collisions. This is where I ran into problem 1. 'Hit Collided with Enemy'... What? After a minute of debugging I identified my error, which I will illustrate below:

raycast-from-within.png

My rays were starting from within the enemy unit's own collider, which would obviously trigger a hit as it travels out of the collider. The result was a bunch of 'blind' enemies who could only see themselves :). How to solve this issue? My first approach was to simply move the start point of all the rays to the edge of the enemy unit using an offset parameter.

raycast-from-single-edge-point.png

This produced the general result I was looking for, but it felt like a slight hack and I didn't like the idea of having to manually adjust the offset for every enemy unit. But I could live with that for an intial implementation, so I decided to do some playtesting before moving on.

I am glad I chose to play it again, because I quickly realized that my enemies lacked almost any peripheral vision.

This meant it was super easy to sneak up on every enemy from the side or behind and made the game pretty trivial. Challenge is going to be core to the experience I am building, so this just is not going to be passable.

no-peripheral.png

At first I thought I could solve this problem by widening up the angle of the cone so I just increased the FOV slider in the inspector. Presto, fixed, right? WRONG.

raycast-wide-angle.png

Overall it felt a little better. Enemies weren't completely blind directly to their sides and navigating through the test area was definitely more difficult. However, you could still be standing right within smelling radius without them seeing you AND it was allowing the enemies to detect the player pretty far off in the periphery or even see around corners. Bad news.

corner-peeking.png

Here is where I decide to take a step back and draw out the problem again on scratch pad. I realize that I can probably use the center of the collider as the starting point for my calculation, but I need the actual start point of the raycast to be at the edge of the collider. 'Can I find that distance without a custom calculations?', I wondered.

At this point I turned to the unity docs for 2d colliders and I find the 'extents' property of collider.bounds. The docs explain that the extents property is a vector and is half the size of the collider in each direction, so I should be able use that to find the edge of the collider. What's that calculation look like....

cone-start-calc.png

I plug roughly this calc into each ray being cast and decide to give it another playtest. The results are exactly what I am looking for. No more enemies chillin with me just hangin out in their awkward blind spot:

no-more-blindspot.png

No more seeing around corners either!

no-more-corner-peeking.png

With these problems solved, I am closing the book on the initial implementation of vision. I am happy with the results, am having fun playing my latest build, and am ready to move on to the next feature!

-TheHocken