I'm currently developing a game where I have 3 types of enemies (Common, Elite and Boss which I'm using tags to define the type). Since I want to give different characteristics and spawning rate to every enemy type, I've been struggling finding a way better to call All enemies instead of calling with tags.
Like:
bool enemyIsAlive()
{
if (GameObject.FindGameObjectWithTag("Common") == null)
{
return false;
}
return true;
}
Here I'm trying to check if there's any enemy in the scene but I can't find a way to reference all enemies. I tried with "Find" but picks the name and the enemies gonna have different names since I can't use the same name for all.
Anyone can help me?
Avoiding using Unity's GameObject Find functions is usually a good thing in the long run for game performance. When handling multiple enemies in a game, I tend to sway towards using a manager class that will be responsible for tracking the spawning and despawning of the enemies and then be used as the source of checking if an enemy is still alive.
For example, lets say you have a simple enemy class that can be either a Common, Elite or Boss. By putting this class onto an object (your enemy) in your hierarchy you can use the type field to say what type this enemy is, and then create a prefab of that enemy to spawn in at runtime.
using UnityEngine;
public class Enemy : MonoBehaviour
{
public Type type;
public enum Type
{
COMMON,
ELITE,
BOSS
}
private void OnEnable()
{
EnemySpawned();
}
private void OnDisable()
{
EnemyDied();
}
public void EnemySpawned()
{
Debug.Log("Enemy Spawned: " + type);
EnemyManager.RegisterEnemy(this);
}
public void EnemyDied()
{
Debug.Log("Enemy Died: " + type);
EnemyManager.UnregisterEnemy(this);
}
}
You can see that when an enemy spawns EnemySpawned()
is called which uses an EnemyManager
to register itself so that it can keep track of it. The same happens when the enemy dies EnemyDied()
is called which then tells the EnemyManager to unregister that enemy.
Here is the EnemyManager class:
using System.Collections.Generic;
public class EnemyManager
{
private static Dictionary<Enemy.Type, int> enemies;
public static void RegisterEnemy(Enemy enemy)
{
if (enemies == null)
{
enemies = new Dictionary<Enemy.Type, int>();
}
if (enemies.ContainsKey(enemy.type))
{
enemies[enemy.type] += 1;
}
else
{
enemies.Add(enemy.type, 1);
}
}
public static void UnregisterEnemy(Enemy enemy)
{
if (enemies == null || !enemies.ContainsKey(enemy.type))
{
return;
}
enemies[enemy.type] -= 1;
}
public static bool IsEnemyTypeAlive(Enemy.Type enemyType)
{
return enemies != null && enemies.ContainsKey(enemyType) && enemies[enemyType] > 0;
}
}
This does not need to be added to an object in the scene hierarchy as it uses static fields and functions.
As you can see the EnemyManager will keep a Dictionary of the enemy types and how many of those enemies are registered. (You could also keep a reference to the Enemy class itself instead of just a count so you could later access those enemies through the EnemyManager too, but for simplicities sake, we will just keep a count)
When an Enemy is registered the count is incremented and when an Enemy is unregistered the count is decremented.
We can then use this to see if enemies are still alive by their type, by checking if the dictionary has firstly had anything registered, then if the dictionary has had that specific enemy type registered, and finally if there are still any registered enemies of that type as they may have all been unregistered.
Here is a demo class then of how that check could be used:
using UnityEngine;
public class Demo : MonoBehaviour
{
private void Update()
{
if (Input.GetKeyDown(KeyCode.Q))
{
Debug.Log("Common Enemy Alive: " + IsCommonEnemyAlive());
}
if (Input.GetKeyDown(KeyCode.W))
{
Debug.Log("Elite Enemy Alive: " + IsEliteEnemyAlive());
}
if (Input.GetKeyDown(KeyCode.E))
{
Debug.Log("Boss Enemy Alive: " + IsBossEnemyAlive());
}
}
public bool IsCommonEnemyAlive()
{
return EnemyManager.IsEnemyTypeAlive(Enemy.Type.COMMON);
}
public bool IsEliteEnemyAlive()
{
return EnemyManager.IsEnemyTypeAlive(Enemy.Type.ELITE);
}
public bool IsBossEnemyAlive()
{
return EnemyManager.IsEnemyTypeAlive(Enemy.Type.BOSS);
}
}
It may seem more complicated, but this solution will scale a lot better than using the GameObject find functions especially if you need to check if the enemies exist often.
To take it even further you could look into System.Action delegates and events to decouple the enemies and the EnemyManager even further and simply raise events when enemies are spawned or die.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments