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. Dismiss Notice

Find Game Objects with a Tag and a Component Set to True?

Discussion in 'Editor & General Support' started by brolol404, Sep 16, 2020.

  1. brolol404

    brolol404

    Joined:
    Feb 14, 2015
    Posts:
    21
    i have a bunch of game objects in a scene with an “enemy” tag. They each have a script component called “controller” on them. That script has a bool “isMoving” that changes from true and false during gameplay.

    I have another unique game object called “manager” with a script component on it called “counter”.


    How do I have the “counter” script get the total number of game objects in the scene that have the “enemy” tag and have “isMoving” set to true within a certain distance from the “manager”?


    Thanks!
     
  2. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    Pseudo-code:
    Code (CSharp):
    1. int GetNumOfMovingEnemies(float withinDistance) {
    2.    int movingEnemies = 0;
    3.  
    4.    //Get all GameObjects tagged as "enemy".
    5.    GameObject[] enemies = FindGameObjectsWithTag("enemy");
    6.  
    7.    for(int i = 0; i < enemies.Length; i++) {
    8.       //Only count GameObjects within the provided distance from this GameObject's position.
    9.       if(PositionWithinDistance(enemies[i].transform.position, withinDistance) {
    10.  
    11.          //Check if the GameObject has a Controller component, and if it does, increment movingEnemies if "isMoving" is true.
    12.          if(enemies[i].TryGetComponent(out Controller controller) && controller.isMoving) {
    13.             movingEnemies++;
    14.          }
    15.       }
    16.    }
    17.  
    18.    return movingEnemies;
    19. }
    20.  
    21. bool PositionWithinDistance(Vector3 position, float distance) => (position - transform.position).sqrMagnitude <= distance;
    Code (CSharp):
    1. void SomeOtherMethod() {
    2.   //Get the number of moving enemies within a distance of 100 units (or whatever you want to set).
    3.   int movingEnemies = GetNumOfMovingEnemies(100f);
    4. }
    There's likely a way to optimize this a bit. If this logic is supposed to run very frequently, I would try and eliminate the
    FindGameObjectsWithTag
    call and have the manager object cache each enemy GameObject instance instead.
     
    brolol404 likes this.
  3. brolol404

    brolol404

    Joined:
    Feb 14, 2015
    Posts:
    21
    Awesome! Thank you!

    What do you mean by "have the manager object cache each enemy GameObject instance?" Is FindGameObjectsWithTag performance intensive?
     
  4. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    All of the
    Find...
    methods are slower than other ways of referencing objects, because they have to search every single GameObject in the scene. There's almost always a better way to get references to objects.

    One way I'm thinking of is creating an "Enemy" script that tracks its own "isMoving" state, as well as invoke two events when it's instantiated & destroyed for the manager object to listen for.
    The manager then keeps a cached List of Enemy scripts that it can loop over, rather than using
    FindGameObjectsWithTag
    every time it needs to check the state of all enemies.
    Example:
    Code (CSharp):
    1. public class Enemy : MonoBehaviour {
    2.    public static event Action<Enemy> OnEnemyCreated;
    3.    public static event Action<Enemy> OnEnemyDestroyed;
    4.  
    5.    public Controller controller;
    6.    public bool IsMoving => controller.isMoving;
    7.  
    8.    private void Start() => OnEnemyCreated?.Invoke(this);
    9.    private void OnDestroy() => OnEnemyDestroyed?.Invoke(this);
    10. }
    Code (CSharp):
    1. public class EnemyManager : MonoBehaviour {
    2.    private List<Enemy> enemies = new List<Enemy>();
    3.  
    4.    private void Awake() {
    5.       Enemy.OnEnemyCreated += AddEnemy;
    6.       Enemy.OnEnemyDestroyed += RemoveEnemy;
    7.    }
    8.  
    9.    private void OnDestroy() {
    10.       Enemy.OnEnemyCreated -= AddEnemy;
    11.       Enemy.OnEnemyDestroyed -= RemoveEnemy;
    12.    }
    13.  
    14.    private void AddEnemy(Enemy enemy) => enemies.Add(enemy);
    15.  
    16.    private void RemoveEnemy(Enemy enemy) => enemies.Remove(enemy);
    17.  
    18.    int GetNumOfMovingEnemies(float withinDistance) {
    19.       int movingEnemies = 0;
    20.  
    21.       for(int i = 0; i < enemies.Count; i++) {
    22.          if(PositionWithinDistance(enemies[i].transform.position, withinDistance) && enemies[i].IsMoving) {
    23.             movingEnemies++;
    24.          }
    25.       }
    26.  
    27.       return movingEnemies;
    28.    }
    29.  
    30.    bool PositionWithinDistance(Vector3 position, float distance) => (position - transform.position).sqrMagnitude <= distance;
    31. }
    This way, the manager is reactive to the number of enemy GameObjects in the scene, and doesn't need to re-search for all of them every time the
    GetNumOfMovingEnemies
    method is called.

    But again, this is probably not necessary if you aren't going to be running this logic very frequently.
     
    brolol404 likes this.
  5. brolol404

    brolol404

    Joined:
    Feb 14, 2015
    Posts:
    21
    Thanks. I plan to use this every update to manage sounds of large armies, so I definitely need to optimize it. The idea is, if more than x enemies are moving, then play "large army footsteps" instead of having every unit have it's own footstep sound effect which becomes a single noise when multiplied by hundreds of units.