1) По тегу. Метод FindGameObjectsWithTag возвращает массив объектов типа GameObject, характеризующихся указанным тегом. Если у вас сложный объект (состоящих из родительского и дочерних), то помечайте тегом только родительский. Минус данного подхода в том, что он относительно медленный, но ощутить его медленность у вас получится на действительно огромных сценах (несколько тысяч/десятков тысяч объектов).
Вот так будет выглядеть код, если вы пометите рут-объект врагов тегом Enemy:
var enemies = GameObject.FindGameObjectsWithTag("Enemy");
if (enemies.Length > 0) {
// действие
}
2. По специфическому скрипту. Проблема тут та же, что и в прошлом пункте, поскольку под капотом они работают почти одинаково.
Скорее всего, для врага у вас есть специальный скрипт-контроллер, который называется, например, Enemy. Тогда код будет таким:
var enemies = FindObjectsOfType<Enemy>();
if (enemies.Length > 0) {
// действие
}
3. Список врагов. Спавня врага, вы добавляете его в список. А перед уничтожеием удаляете из него.
Создание - скрипт спаунера:
public List<Enemy> enemies = new List<Enemy>();
// ...
var enemy = Instantiate(...);
enemies.Add(enemy);
// чтобы скрипт врага мог общаться с контроллером, передадим туда ссылку
enemy.spawner = this;
// и метод логики удаления
public void Remove(Enemy e) {
enemies.Remove(e);
if (enemies.Count > 0) {
// действие
}
}
Уничтожение, скрипт врага:
public Spawner spawner;
// ...
if (health <= 0) {
spawner.Remove(this); // удаляем этого врага из списка спаунера
Destroy(gameObject); // уничтожаем его объект
}
Третий способ самый выгодный с точки зрения производительности, так как вы имеете список, где есть только враги.