Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Dealing with multiple gameobjects inside Trigger (improving "Enemy Sight" script)

Discussion in 'Scripting' started by GoneAway, Nov 17, 2015.

  1. GoneAway

    GoneAway

    Joined:
    Apr 17, 2015
    Posts:
    4
    A small introduction first. The past few months I am playing around with Unity and in the process of that I am learning C# and everything related to Unity. Which means that as a beginner in coding, I am taking the scripts from tutorials here and there and trying to fit to my needs. And that leads me to this post.
    I grabbed the original script from https://unity3d.com/learn/tutorials/projects/stealth/enemy-sight?playlist=17168. which is dealing with just one gameobject(being the player in this case) and I am trying to configure it so it can deal when you have multiple gameobjects entering the trigger.
    So assume you are the player and there are multiple enemies coming at you, and you can attack only what you see. The final point is that we need to maintain a line of sight. My result so far is this:


    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class Player : MonoBehaviour
    6. {
    7.     public float fieldOfViewAngle = 110f;   // Number of degrees, centred on forward, for the enemy see.
    8.     public bool enemyInSight;               // Whether or not the player is currently sighted.
    9.     public Vector3 personalLastSighting;    // Last place this enemy spotted the player.
    10.  
    11.     private NavMeshAgent agent;             // Reference to the NavMeshAgent component.
    12.     private SphereCollider col;             // Reference to the sphere collider trigger component.
    13.  
    14.     //Create a list to hold the enemies entering the trigger.
    15.     public List<GameObject> enemiesInTrigger = new List<GameObject>();      
    16.  
    17.     void Awake()
    18.     {
    19.         col = GetComponent<SphereCollider>();
    20.         agent = GetComponent<NavMeshAgent>();
    21.     }
    22.  
    23.     void Update()
    24.     {
    25.         //If an enemy is inside the field of view
    26.         if(enemyInSight)
    27.         {
    28.             //do something
    29.         }
    30.     }
    31.  
    32.     void OnTriggerStay(Collider other)
    33.     {
    34.         // If the enemy has entered the trigger sphere..
    35.         if (other.gameObject.CompareTag("Enemy"))    
    36.         {
    37.             // By default the enemy is not in sight.
    38.             enemyInSight = false;
    39.             // If the enemy is not already in the list
    40.             if (!enemiesInTrigger.Contains(other.gameObject))
    41.             {
    42.                 // Add him to the list          
    43.                 enemiesInTrigger.Add(other.gameObject);
    44.             }        
    45.             //Go through the list
    46.             for (int i = 0; i < enemiesInTrigger.Count; i++)        
    47.             {
    48.                 // Create a vector from the player to the enemies and store the angle between them and forward.
    49.                 Vector3 directionToEnemy = enemiesInTrigger[i].transform.position - transform.position;
    50.                 float angle = Vector3.Angle(directionToEnemy, transform.forward);
    51.                 // If the angle between forward and where the enemies are, is less than half the angle of view...
    52.                 if (angle < fieldOfViewAngle * 0.5f)
    53.                 {
    54.                     RaycastHit hit;
    55.                     // ... and if a raycast towards the enemies hits something...
    56.                     if (Physics.Raycast(transform.position, directionToEnemy.normalized, out hit, col.radius))
    57.                     {
    58.                         // ... and if the raycast hits an enemy...
    59.                         if (hit.collider.CompareTag("Enemy"))
    60.                         {
    61.                             // ... that enemy is in sight.
    62.                             enemyInSight = true;
    63.                             // Set the last sighting as the enemy current position
    64.                             personalLastSighting = enemiesInTrigger[i].transform.position;
    65.                         }
    66.                     }
    67.                 }        
    68.             }
    69.         }
    70.     }
    71.  
    72.     void OnTriggerExit(Collider other)
    73.     {
    74.         if (other.gameObject.CompareTag("Enemy"))       //If the enemy exits the trigger.
    75.         {
    76.             enemiesInTrigger.Remove(other.gameObject);      //Remove from the list.
    77.             enemyInSight = false;           //The enemy is no longer in sight.
    78.         }
    79.     }
    80. }
    81.  

    I have not yet stopped improving it. There are a few issues here and there. So if anyone could come up with any suggestions,or sees any errors, I could edit it appropriately here, and it could be useful for anyone that wants to add this function in his project.
     
    Last edited: Nov 18, 2015
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,200
    First of all you're comparing the tag "Enemy" in OnTriggerEnter/Stay, but "Human" in exit...

    Second of all, what's up with your OnTriggerStay method? You're checking if what you're colliding with has the tag "Enemy", but you're iterating the enemiesInTrigger list to actually do the logic.


    So, every time an enemy enters the trigger, you add it to a list. Then, for every enemy inside the trigger, you iterate that list, and set some public variables. This means that if there's 4 enemies inside the trigger, you're figuring iterating over the enemiesInTrigger list 4*4=16 times. If there's 10 enemies, you're iterating it 100 times.

    You seem to have combined two possible solution types to the "what enemies can I see" problem:
    solution 1: Use OnTriggerEnter/Exit to figure out what's inside range, and iterate over that list in Update to figure out what you can see
    solution 2: Use OnTriggerStay to check what's inside the trigger.

    Both of those work, but using both at the same time leads to a crazy extra amount of work. You'll need to stick to one of them.

    Another problem: you're setting enemyInSight false for every enemy, and then true if you can see it. This means that if you can't see the last enemy in the list (the last enemy you came into contact with), that variable will be false, no matter if you can see the earlier enemies or not.
     
  3. GoneAway

    GoneAway

    Joined:
    Apr 17, 2015
    Posts:
    4
    I actually forgot to edit that line before posting the script.

    I got rid of the OnTriggerEnter method. I want to stick with OnTriggerStay method, instead of working the logic inside Update. I edited my OP with the current script I have.

    I haven't met this issue. Everytime an enemy was in the line of sight, the enemyInSight was setting to true and everytime an enemy was out of line of sight the boolean was setting to false.

    I guess I should have mentioned on my OP that I want to keep track of the positions of each enemy entering the trigger and as soon as someone is in my line of sight do something, like shooting, or chasing it, or w/e. Thank you for your reply.