3
\$\begingroup\$

I ran into a trouble while making a field of view for my character. I figured how to make it work with a Linecast, but what I really need is a cone-shaped field, so that the character can detect objects or enemies if they get in that field of vision. It also needs to be intersectable by other objects, so OnCollisionEnter won't work. I suspect that Raycasting might solve the problem, but I couldn't quite understand it's workings, because I'm still new. I would really appreciate any ideas that might help to solve it. Here is what I need:

collision detector

Here is what I have so far, but this detector is unsuitable, because it can only detect objects in a straight line:

public Transform sightStart, sightEnd;
    // Update is called once per frame
    public bool objectSpotted = false;
    void Update () {
        Raycasting1 ();
}
void Raycasting1()
{
    Debug.DrawLine(sightStart.position, sightEnd.position, Color.green);
    objectSpotted = Physics2D.Linecast (sightStart.position, sightEnd.position);
}
\$\endgroup\$
  • \$\begingroup\$ Try googling frustum or frustum culling \$\endgroup\$ – Niels Mar 22 '16 at 7:05
5
\$\begingroup\$

Physics.OverlapSphere() and Physics2D.OverlapCircleAll() will return all colliders within a radius of a point. Those colliders are in range, but possibly not in view. If you are going to do this often, create an array once, and pass it to Physics2D.OverlapCircleNonAlloc(), instead.

To see if they are in view, check the dot-product of the normalized collider-to-center direction and the normalized character-forward direction. The dot is 1.0 if the object is directly in front of the character (0 degrees), 0.0 if the object is directly left/right of the object (+-90 degrees), and -1.0 if the object is directly behind (+-180 degrees); it is the cosine of the angle between the object and forward.

enter image description here

Edit:

void Raycasting1()
{
    Collider2D[] cols = Physics2D.OverlapCircleAll(transform.position, 1);
    Vector3 characterToCollider;
    float dot;
    foreach (Collider2D collider in cols)
    {
        characterToCollider = (collider.transform.position-transform.position).normalized;
        dot = Vector3.Dot(characterToCollider, transform.forward);
        if (dot >= Mathf.Cos(55))
        {
           //objectSpotted = true;
        }
        else
        {
           //objectSpotted = false;
        }
    }
}

For many, many objects:

Collider2D[] inRangeColliders = new Collider2D[1000]; //No more than 1000
void Raycasting1()
{
  int inRangeColliderCount =
     Physics2D.OverlapCircleNonAlloc(transform.position, 1, inRangeColliders);
  for (int i = 0; i < inRangeColliderCount; i++) //replace the foreach
  {
     //collider. is now inRangeColliders[i].
     ...
  }
}
\$\endgroup\$
  • \$\begingroup\$ Don't want to seem like a totally ignorant person, but how do I implement Physics2D.OverlapCircleAll() in code? I looked up some examples, but not sure what some variables stand for and what values to assign for them. I Basically just need a Boolean that could show whether or not a specific type of objects is in the fov. \$\endgroup\$ – Hellsia Mar 24 '16 at 3:16
  • \$\begingroup\$ @Jon On another topic (and I'm sorry for that), what did you use to generate this image? Because it's impressive how clear your diagram is. \$\endgroup\$ – lvictorino Mar 24 '16 at 16:20
  • \$\begingroup\$ @lvictorino, I use AutoCAD 2016 for 2D and simple 3D stuff. Thanks! \$\endgroup\$ – Jon Mar 24 '16 at 17:53
  • \$\begingroup\$ @Jon That is so weird... This code gives 14 errors... Starting from "invalid token 'foreach' in class, struct or interface" and so on... \$\endgroup\$ – Hellsia Mar 24 '16 at 21:24
  • \$\begingroup\$ @Hellsia, this script should be attached to the player so there is no reason for objectSpotted. That being said, the "downstream" behavior of this is undefined. What is supposed to happen when the character detects an enemy? \$\endgroup\$ – Jon Mar 24 '16 at 22:39
2
\$\begingroup\$

First of all... don't over complicate the objective... I would suggest creating your own collider, and not using one from unity...

First of all, get all of the enemies and put them in their own separate list.

public void EnemyList(){
    GameObject[] GameObjectArray = FindObjectsOfType (typeof(GameObject)) as GameObject[];  // we check every gameobject and only select the Enemies
    unitList = new List<GameObject> ();                                                     // and put them in their own separate list for quicker checking
    for (int i = 0; i < GameObjectArray.Length; i++) {                                      
        if (GameObjectArray [i].layer == unitLayer)
            unitList.Add (GameObjectArray [i]);
    }
}

Then we can check if they are within sight... the condition to check if we should check if they are in our fov...

// check to see if a enemy is within viewing range
public bool IsEnemyNear(int i){
    float dist = Vector3.Distance (unitList [i].transform.position, transform.position);
    if (dist < unitDistanceFromNpc) // if true, an Enemy is within range
        return true;
    else return false;
}

If the enemy is near and within the player's viewing range.. then we only need to shoot ONE Raycast... Cause we know where he is... and if he is hiding or behind crates or barrels.. then we cannot see him...

// check to see if Enemy is in npc's current fov
public bool IsEnemyInNpcFOV(int i){
    Vector3 targetDir = unitList[i].transform.position - transform.position;
    Vector3 forward = transform.forward;
    float angle = Vector3.Angle(targetDir, forward);
    if (angle < npcFov)
        return true;
    else return false;
}

and if the NPC can see the player...

// can the Npc see the Player
PlayerSeen = false;
public void IsPlayerSeen(int i){
    Vector3 direction = ( unitList[i].transform.position - transform.position ).normalized;
    if ((Physics.Raycast (ourPosition, direction, out hit, unitDistanceFromNpc) && hit.collider.tag == "Player")) {
        hit.collider.gameObject.GetComponent<Unit> ().isSeen = true;
        PlayerSeen = true;
    }
}

Basically, you create your own collider.. if the enemy is range of that collider then you check if it's in your fov.. and if it is, then you check if you can see him... i.e... he is not behind a pile of crates.... a simple loop.. and only one raycast.... hope this helps....

\$\endgroup\$
  • \$\begingroup\$ Thanks for the suggestion, however I'm not sure if I'm getting it right. I tried implementing this code, but I can't get the bools to change their value. \$\endgroup\$ – Hellsia Mar 24 '16 at 2:40
  • \$\begingroup\$ I may have over complicated the answer.... my bad.. and just so you know.. I drew this pic on the whiteboard at my work last week, so I know what your asking.. tinypic.com/view.php?pic=2mwy2b&s=9#.VvRkQ_krKUk what your looking for is this docs.unity3d.com/ScriptReference/Vector3.Angle.html It will return the angle of the target from your current transform in 180 degrees, not 360... so if your fov is lets say 60.. you just check if the angle returned is less that 30 or more that 150... then the object is in your fov. simple as that. \$\endgroup\$ – RayBan Mar 24 '16 at 22:04
  • \$\begingroup\$ Honestly, by this point I'd be happy even with just a 360 fov, as long as it can detect other objects with colliders on them from a distance, without actual physical colliding... I feel like I have to study everything there is about Unity first, before I'll be able to figure this single little thing out... lol. Thank you for your efforts though, I really appreciate it. \$\endgroup\$ – Hellsia Mar 26 '16 at 14:25
  • \$\begingroup\$ I think I answered this... but again, I may be over complicating it... \$\endgroup\$ – RayBan Mar 28 '16 at 1:07
  • \$\begingroup\$ I need to see your code.... otherwise I cannot help you with this... post your code... \$\endgroup\$ – RayBan Mar 28 '16 at 1:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.