Raycast Question

I'm trying to figure out how to represent a field of view for an enemy character. As it moves around i want it to only be able to "see" a 180 degree hemisphere in front of it (this is for spotting the player not pathfinding). Is it possible to raycast in more than one direction or do i have to use multiple raycasts? Should i use a child object that rotates the ray (like a lighthouse)?

At first I though you could probably move a spare camera to the character and then ask it what it sees. There doesn't appear to be a built-in method for asking a camera that, though it still might be fun as a debugging tool to be able to adopt an enemy's view or project their view somewhere.

Another method might be to create a mesh collider the shape of the enemy's view fustrum. I think this method has too many problems, though it's entertaining to think of oddly shaped views, such as two cylinders as if the enemy is looking through binoculars.

It turns out that the math for what you are requesting isn't difficult. There are different view shapes that would require different formula, but if you actually just want a hemisphere shape, you just need two tests. Test 1: is the object inside the full sphere? This is just a distance test.

Test 2: Is the object in front of the plane that bisects the sphere? This is also easy since you already have the elements that define the plane: a point (namely where the enemy is standing) and a ray (the direction the enemy is looking.)

If an object passes both tests then the enemy can see it.

A cone-shaped view should also be easy to implement and might give you a better approximation to a viewing area. It's centered around the same ray as the hemisphere. You would compute the distance from the ray to see if the object is inside the cone, plus you probably also want to compute distance from the enemy to see if it's too far away to see.

However you do it, it might also be fun to add Unity debugging aids to visualize each enemy's view. Gizmos to see it in the scene view, or just draw regular lines/rays to see it in the game itself.

[edit - removed some discussion of view fustrum shapes that weren't what was asked for.]

thanks for the reply.

[quote]
If an object passes both tests then the enemy can see it.
[/quote]

would this type of setup take into account obects directly between the enemy player collider? from what i could find in the forums, i was under the impression that raycasting was the typical method for finding other objects - i have that basic set up working but it only finds you if you run directly in front of it.

also, if the map is large enough the viewing distance equally large, than the view mesh would have to be huge - i don't know if that presents a problem or not with physics calculations etc.

it would be nice to have the view mesh as a gizmo, i think i'm going to add that whatever method is figured out.

I you just want if the enemy could see the player you could also probably do the following brute force method:
1. Have a reference to the player in your enemy script
2. Create a vector between the player and the enemy.
3. Create a vector pointing directly forward of the enemy or directly to the side.
3. Use the Vector3.AngleBetween() using the vector between the enemy and the player and the vector pointing forward (or sideways) of the enemy.
4. See if the angle falls withing a "viewing" angle. Note: AngleBetween() return is in Radians.
5. If it does, cast a ray towards the user to make sure the view is not obstructed (might actually want to do this one first).

If you want to see for multiple "character" type objects, then just iterate through them. You could also have a controller type object that keeps the references if you wanted to do it that way.

would this type of setup take into account obects directly between the enemy player collider? from what i could find in the forums, i was under the impression that raycasting was the typical method for finding other objects - i have that basic set up working but it only finds you if you run directly in front of it.

also, if the map is large enough the viewing distance equally large, than the view mesh would have to be huge - i don't know if that presents a problem or not with physics calculations etc.
[/quote]

Raycasting works well for some situations, sure. If you want to see if a bullet or a laser hit something, or if you want to see if the mouse is hovered over an object (and which one.) It's easy in those cases because you have a single ray/line and want to see what it hits first.

In your case you want to find if an object (the player) is in the view of the enemy. One raycast won't work because you are testing an area. You could approximate by casting a series of rays, maybe, and hope you don't miss the target by a small amount. But in the end you will have done as much work as te "right" way.

Our proposed method is purely mathematical. It's not dependent on the mesh resolution, screen resolution, or physics.

The first test is that the player (point P) is within range. Just calculate the distance between P and the enemy (point E) using the Unity distance function. Here's an example I stole from the Unity reference. In this example transform.position is the player's position.

if ( Vector3.Distance( enemy.position, transform.position ) < 10 ) {
            print("I sense the enemy is near!");
        }

The second test is to test that the player is in-front of the enemy. Ifrog just suggested a good solution to that, by computing the angle between the enemy "look at" vector and the vector defined by the enemy and player positions.

Here is an interesting question to add here, when the raycast is fired at the enemy to test for obstructions, is there any way yet in Unity to get how much light is falling on the enemy's material? Would be a good feature to be able to find out how much light something is in. (Thief, Splinter cell, etc)

-Jeremy

Well you could always just calculate this manually. Get the light source, the angle it is rotated out, then work out a little formula for how close to the center of any of the lights the model is at. React accordingly. Or something like that anywhoo. Heh it's late. But no other then setting it up yourself I don't think there is any built in way to do this.

That wouldn't work very well when you have many lights in the scene, and you also have to account for falloff, etc. If you have moving lights, flickering lights, etc, you are stuck.

-Jeremy

Then work out some other way to do it :P

That's why I posted the original question. :P

Hmmm interesting problem...

-Jeremy

Well you could always design scenes with guaranteed safe areas. Then use a bounding box around those. Character in = invisible. Or maybe even use that raycasting stuff from each light just like the guard's view range. If both see the player adequately, bam alarm.

sorry for the delay in replying, my day job programming noobness were working against me.

anyway, thanks to your suggestions i got it working with this:

var target : Transform;
function Update () {
    playerDir = target.position - transform.position;
    var hit : RaycastHit;
    if (Physics.Raycast (transform.position, playerDir, hit, playerDir.magnitude)) {
        Debug.DrawLine (transform.position, hit.point, Color.black);
        if (hit.collider.transform.CompareTag ("Player")) {
            Debug.DrawLine (transform.position, hit.point, Color.red);
            print ("Player Is in line of sight.");
            var fov = Vector3.AngleBetween (transform.TransformDirection (Vector3.forward), playerDir) * Mathf.Rad2Deg;
            if (fov < 90) {
                Debug.DrawLine (transform.position, hit.point, Color.yellow);
                print ("Player is in field of view.");
                if (playerDir.magnitude < 5) {
                    Debug.DrawLine (transform.position, hit.point, Color.green);
                    transform.LookAt (target);
                    print ("Player is in range, player seen.");
                }
            }
        }
    }
}

it seems to work really well, but if there's anything that could be done better please let me know.

thanks again i would not have figured that out without your help ; )

Looks good. Some other ideas:

The constants 90 (degrees) and 5 (distance) could be made into public variables. Then you could tweak their values with the inspector, have some enemies be more observant than others, or have the attentiveness of an enemy vary (at nighttime enemy can't see as far.)

Personally I would take put the logic into a new function, bool CanSeePlayer( ) that is called from Update. Update methods tend to get a lot of logic in them so it's nice to break them down, and it makes the code much more self-documenting.

You could even generalize the previous function further:
bool CanSeeObject(GameObject obj)
Then you could test whether enemies see powerups or each other (if player shoots enemy A, enemy B might see enemy A falling down even if B can't see the player.)

If you really want to go overboard, fire two more rays out of the sides of the enemy's head, representing ears. If the player makes a loud sound and it's within 45 degrees or so of an ear ray, the enemy can turn to look. :-)

Or you could give the player a general stealth value. If sneaking modify value thus. Is in shadow blah. Night time so and so. That way even at night with the right tweaking you could sneak up on the guard and slit his throat.