I am currently working on modifying my raycasting for my enemy AI!
Basically, I have a few enemies that move around my level and they spot the player using simple Physics.Raycast which I project from their heads and if it collides with the player the player is ‘spotted’ - everything from that aspect works great!
I have been working now on some sort of field of view code, which will allow the enemy to see the player if they are anywhere inside of their field of view. I tried this using a sphere collider, and raycasting again then checking against some predefined field of view angles against the radius of the sphere and this did not end well… lol
I am essentially just trying to have it so, instead of a single line, my enemies can see a little bit more, ideally if the player is just off to the side, and the enemy runs towards them, but the raycast totally misses the player… it doesn’t feel organic.
Also worth mentioning, they aren’t actually looking for the players collider, I have a much larger box attached to the player which is the trigger that the raycasts are looking for. This helped quite a bit with having the enemies spot my player, but there are still tons of dead spots, that I think needs to be dealt with using trigonometry to an extent!
Thanks for any input guys! I really appreciate the feedback!
I was thinking about this problem earlier, and I think (haven’t tried yet) a box collider that’s been edited into more of a pyramid, and use that as the field of view. You could then raycast to the player with various offsets (star-pattern, or something) for line of sight. It may not be fool-proof, but you’ll get the benefit of not sending hundreds of raycasts each frame while still covering an area to see if the player is actually visible.
An alternative to (1) and (2) is to create your own custom collider in 3D if you know how to use a 3D program, then use the mesh collider on it in Unity, although this comes with it’s own drawback of potential performance drop.
So the idea is, thing enters sight range, enemy checks if thing is within calculated FOV, enemy shoots ray at thing to check if there are obstructions, react only if thing is within FOV and there are no obstructions
Method 2 sounds very close to what I was attempting. Do I still need a spherical collider? My only reservation with using a spherical collider is… my level has some long hallways and is multi floored. In order to achieve the range that I would need for the raycast portion of the logic… by using the radius as the range parameter… I would need a HUGE sphere which could possibly (at least in my head when I run the pseudo code) detect the player across various floors, which would not be ideal.
I didn’t see anything about using a sphere collider in the second method that is why I was curious!
I would evaluate if the target is in the AI’s field of view mathematically, and then do your raycast to make sure the AI actually has line of sight (aren’t around a corner or behind a pillar or something).
Here’s some code:
float distance = 100.0f; // how far they can see the target
float arc = 45.0f; // their field of view
if(Vector3.Distance(ai.position, player.position) < distance)
{
// enemy is within distance
if(Vector3.Dot(ai.forward, player.position) > 0 && Vector3.Angle(ai.forward, player.position) < arc)
{
// enemy is ahead of me and in my field of view
// do your raycast
}
}
You could get fancy and project 2 rays, one from each eye – but that’s probably a bit much.
Yes I have a raycast but I guess I am still confused with this… Wouldn’t this still give me the same issue? In order for the raycast to hit the player the single ray must hit the player. I’m not sure how this will help me, wouldn’t putting my raycast in the Vector.Dot if conditional still project the raycast like it normally does and the AI still won’t see me if I am in the FOV?
Instead of using a raycast that’s projected forward from the AI’s head, raycast between the AI’s head and the player’s position – if you do that after the field of view check, then you know for certain that the player is both within their field of view and in their direct line of sight.
Ah!! That makes way more sense… so the raycast will always be directed at the player and I am using the FOV to calculate whether or not the ray is within the pre-defined angle…
Here’s my police officer looking for the bad guys:
Here’s the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FoV : MonoBehaviour
{
public GameObject[] PotentialTargets;
public Material Green;
public Material Red;
protected float rotation = 120;
protected void Update()
{
rotation += 36.0f * Time.deltaTime;
transform.rotation = Quaternion.Euler(0, rotation, 0);
foreach(GameObject target in PotentialTargets)
{
if (CanSee(target) == true)
ChangeMaterial(target, Red);
else
ChangeMaterial(target, Green);
}
}
protected void ChangeMaterial(GameObject target, Material material)
{
Renderer renderer = target.GetComponent<Renderer>();
renderer.material = material;
}
protected bool CanSee(GameObject target)
{
float distance = 100.0f; // how far they can see the target
float arc = 45.0f; // their field of view
if (Vector3.Distance(transform.position, target.transform.position) < distance)
{
// enemy is within distance
if (Vector3.Dot(transform.forward, target.transform.position) > 0 && Vector3.Angle(transform.forward, target.transform.position) < arc)
{
// enemy is ahead of me and in my field of view
RaycastHit hitInfo;
// Vector3(0, 0.5f, 0) is the head offset for my capsules, you'll need to adjust accordingly
if (Physics.Raycast(transform.position + new Vector3(0, 0.5f, 0), (target.transform.position + new Vector3(0, 0.5f, 0)) - transform.position, out hitInfo) == true)
{
// we hit SOMETHING, not necessarily a player
if (hitInfo.collider.name == "Player")
return true;
}
}
}
return false;
}
}
You’ll mainly be interested in the CanSee() function. You might need to do additional raycasts, depending on what your characters look like – like one aimed at their head, one at their chest, one at their legs.
Okay I had it printing out just fine until I tried modifying my Ray!
Any thoughts?
This is my code
function FixedUpdate()
{
if(Vector3.Distance(transform.position, player.transform.position) < distance)
{
//player is within distance
//print("Player is within distance");
if(Vector3.Dot(transform.forward, player.transform.position) > 0 && Vector3.Angle(transform.forward, player.transform.position) < arc)
{
//Player is ahead of AI AND in AI FOV
//print("Player in front and in FOV of AI");
var ray = new Ray(transform.position, player.transform.TransformDirection(Vector3.forward) - transform.position);
if(Physics.Raycast(ray, hit))
{
if(hit.collider.gameObject.tag == "seeme")
{
print("THE RAY IS HITTING THE PLAYER");
}
}
}
}
}
For reference, this is how I was drawing the raycast, and a debug.drawline showed it in the exact spot that I need it! Without any kind of buffers. So I was trying to use my raycast with the new information based on arc and distance!
var ray = new Ray(transform.position, transform.TransformDirection(Vector3.forward));
if(Physics.Raycast(ray, hit, range))
{
if(hit.collider.gameObject.tag == "seeme")
{
print("You have been spotted!");
}
}
After more closely testing the Vector3.Dot I realized that something was reading incorrectly and earlier when I was implementing it, I saw the read out in the console and assumed it was all good!
Creating a playerDirection variable and passing that into the Vector3.Dot worked perfect!!!
Now the only thing not firing still is this dang Physics.Raycast!
This is my entire FixedUpdate for the AI which search for the player! After extensive testings the Distance and Dot checks are working perfect! I just need to iron out my Raycasting!
function FixedUpdate()
{
if(Vector3.Distance(transform.position, player.transform.position) < distance)
{
//player is within distance
//print("Player is within distance");
var playerDir = (player.transform.position - transform.position).normalized;
if(Vector3.Dot(transform.forward, playerDir) > 0 && Vector3.Angle(transform.forward, playerDir) < arc)
{
//Player is ahead of AI AND in AI FOV
// print("Player in front and in FOV of AI");
var ray = new Ray(transform.position, player.transform.position - transform.position);
if(Physics.Raycast(ray, hit))
{
if(hit.collider.gameObject.tag == "seeme")
{
print("THE RAY IS HITTING THE PLAYER");
}
}
}
}
}
Any input or assistance with the Raycast would be phenomenal!