- Home /
making a left enemies counter,count the number of enemies
Hi, I am working on a FPS game where enemies spawn in waves, and i want to make a counter for the number of enemies left to kill in the scene, and i do it by creating a var then finding the GameObjects with tag "Enemy" and adding it to the list or (var), but the problem with that is the length of the list wont update if i kill one of them, or is there a better way of doing the counter.
Here is the enemy script:
using UnityEngine; using UnityEngine.AI; using System.Collections;
public class EnemyController : MonoBehaviour {
Transform target;
NavMeshAgent agent;
void Start()
{
target = PlayerManager.instance.Player.transform;
agent = GetComponent<NavMeshAgent>();
}
void Update()
{
targetPlayer();
}
void targetPlayer()
{
agent.SetDestination(target.position);
}
and i know that this wave spawner script is way too long but i dont know what parts are the important for you, sooo.......
here it is:
using UnityEngine; using System.Collections; using UnityEngine.UI; using System; using System.Collections.Generic;
public class WaveSpawner : MonoBehaviour {
//public GameObject spawningNextWavePanel;
//public Text spawningNextWaveText;
public GameObject waveCompletedPanel;
public Text waveCompletedText;
public enum SpawnState { SPAWNING, WAITING, COUNTING };
[System.Serializable]
public class Wave
{
public string name;
public Transform enemy;
public int count;
public float rate;
}
public Wave[] waves;
private int nextWave = 0;
public int NextWave
{
get { return nextWave + 1; }
}
public Transform[] spawnPoints;
public float timeBetweenWaves = 5f;
public float waveCountdown;
public float WaveCountdown
{
get { return waveCountdown; }
}
private float searchCountdown = 1f;
private SpawnState state = SpawnState.COUNTING;
public SpawnState State
{
get { return state; }
}
void Start()
{
if (spawnPoints.Length == 0)
{
Debug.LogError("No spawn points referenced.");
}
waveCountdown = timeBetweenWaves;
}
void Update()
{
//var enemies = GameObject.FindGameObjectsWithTag("Enemy");
//var enemiesCount = enemies.Length;
//Debug.Log(enemiesCount);
if (state == SpawnState.WAITING)
{
if (!EnemyIsAlive())
{
WaveCompleted(waves[NextWave - 1]);
}
else
{
return;
}
}
if (waveCountdown <= 0)
{
if (state != SpawnState.SPAWNING)
{
StartCoroutine( SpawnWave ( waves[nextWave] ) );
}
}
else
{
waveCountdown -= Time.deltaTime;
}
}
void WaveCompleted(Wave myWave)
{
Debug.Log("Wave Completed!");
waveCompletedPanel.SetActive(true);
waveCompletedText.text = myWave.name + " Completed";
StartCoroutine("waitForText");
state = SpawnState.COUNTING;
waveCountdown = timeBetweenWaves;
if (nextWave + 1 > waves.Length - 1)
{
nextWave = 0;
Debug.Log("ALL WAVES COMPLETE! Looping...");
}
else
{
nextWave++;
}
}
IEnumerator waitForText()
{
yield return new WaitForSeconds(2);
waveCompletedPanel.SetActive(false);
}
private string GetName()
{
return name;
}
bool EnemyIsAlive()
{
searchCountdown -= Time.deltaTime;
if (searchCountdown <= 0f)
{
searchCountdown = 1f;
if (GameObject.FindGameObjectWithTag("Enemy") == null)
{
return false;
}
}
return true;
}
IEnumerator SpawnWave(Wave _wave)
{
//spawningNextWavePanel.SetActive(true);
//spawningNextWaveText = "Spawning " + _wave.name + " in " + waveCountdown;
Debug.Log("Spawning Wave: " + _wave.name);
state = SpawnState.SPAWNING;
for (int i = 0; i < _wave.count; i++)
{
SpawnEnemy(_wave.enemy);
yield return new WaitForSeconds( 1f/_wave.rate );
}
state = SpawnState.WAITING;
yield break;
}
void SpawnEnemy(Transform _enemy)
{
Debug.Log("Spawning Enemy: " + _enemy.name);
Transform _sp = spawnPoints[UnityEngine.Random.Range (0, spawnPoints.Length) ];
Instantiate(_enemy, _sp.position, _sp.rotation);
}
}
hope you can help since i tried to modify it my self and failed
Answer by Hellium · Jul 14, 2019 at 07:05 AM
OnDestroyEventSender.cs
using UnityEngine;
public class OnDestroyEventSender : MonoBehaviour
{
public event System.Action<OnDestroyEventSender> OnDestroyed;
private void OnDestroy()
{
if ( OnDestroyed != null )
OnDestroyed( this );
}
}
Wave.cs
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public class Wave
{
/// <summary>
/// Event invoked when the wave has been cleared
/// </summary>
public event System.Action<Wave> OnCleared;
/// <summary>
/// Event invoked when an enemy of the wave has been spawned
/// </summary>
public event System.Action<Wave, Transform> OnEnemySpawned;
/// <summary>
/// Event invoked when an enemy of the wave has been killed
/// </summary>
public event System.Action<Wave, Transform> OnEnemyKilled;
[SerializeField]
private string name;
[SerializeField]
private Transform enemyPrefab;
[SerializeField, Range(1, 100)]
private int count;
[SerializeField, Range(0.01f, 1f)]
private float rate;
private List<OnDestroyEventSender> enemies;
public bool IsCleared
{
get { return enemies != null && enemies.Count == 0; }
}
public string Name
{
get { return name; }
}
public int Count
{
get { return count; }
}
public float Rate
{
get { return rate; }
}
public int RemainingEnemiesToInstantiate
{
get; private set;
}
public int RemainingEnemiesAlive
{
get { return enemies.Count; }
}
/// <summary>
/// Resets the wave
/// </summary>
public void Reset()
{
RemainingEnemiesToInstantiate = count;
}
/// <summary>
/// Instantiate an enemy at the given position and rotation
/// </summary>
public void InstantiateEnemy( Vector3 position, Quaternion rotation )
{
if ( RemainingEnemiesToInstantiate == 0 )
throw new System.InvalidOperationException( "No remaining enemy to instantiate. Have you called `Reset`?" );
if ( enemies == null ) enemies = new List<OnDestroyEventSender>( count );
Transform enemy = Object.Instantiate( enemyPrefab, position, rotation );
OnDestroyEventSender onDestroyEventDispatcher = enemy.gameObject.AddComponent<OnDestroyEventSender>();
onDestroyEventDispatcher.OnDestroyed += OnEnemyDestroyed;
enemies.Add( onDestroyEventDispatcher );
RemainingEnemiesToInstantiate--;
if ( OnEnemySpawned != null )
OnEnemySpawned( this, enemy );
}
/// <summary>
/// Called when an enemy has been destroyed
/// </summary>
private void OnEnemyDestroyed( OnDestroyEventSender destroyedEnemy )
{
destroyedEnemy.OnDestroyed -= OnEnemyDestroyed;
enemies.Remove( destroyedEnemy );
if ( OnEnemyKilled != null )
OnEnemyKilled( this, destroyedEnemy.transform );
if ( RemainingEnemiesToInstantiate == 0 && enemies.Count == 0 && OnCleared != null )
OnCleared( this );
}
}
WaveSpawner.cs
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class WaveSpawner : MonoBehaviour
{
[Header("===== Waves ===== ")]
public GameObject waveCompletedPanel;
public Text waveCompletedText;
public Text waveEnemiesCountText;
public Wave[] waves;
public float timeBetweenWaves = 5f;
public bool loopWaves = false;
private int currentWaveIndex = -1;
private float waveCountdown;
private bool spawningWave;
[Header("===== Spawn points ===== ")]
public Transform[] spawnPoints;
/// <summary>
/// Gets the current wave
/// </summary>
private Wave CurrentWave
{
get
{
return currentWaveIndex >= 0 && currentWaveIndex < waves.Length
? waves[currentWaveIndex]
: null;
}
}
/// <summary>
/// Indicates whether the last wave has been cleared (if waves don't loop)
/// </summary>
private bool ClearedLastWave
{
get { return !loopWaves && currentWaveIndex == waves.Length - 1 && CurrentWave.IsCleared; }
}
void Start()
{
if ( spawnPoints.Length == 0 )
Debug.LogError( "No spawn points referenced." );
waveCountdown = timeBetweenWaves;
}
void Update()
{
waveCountdown -= Time.deltaTime;
if ( spawningWave )
{
if ( waveCountdown <= 0 && CurrentWave != null && CurrentWave.RemainingEnemiesToInstantiate > 0 )
{
Transform spawnPoint = spawnPoints[UnityEngine.Random.Range (0, spawnPoints.Length) ];
CurrentWave.InstantiateEnemy( spawnPoint.position, spawnPoint.rotation );
waveCountdown = 1 / CurrentWave.Rate;
}
}
else if ( waveCountdown <= 0 )
{
StartNextWave();
}
}
/// <summary>
/// Starts the next wave
/// </summary>
private void StartNextWave()
{
Debug.Log( "Starting next wave!" );
spawningWave = true;
if ( CurrentWave != null )
{
CurrentWave.OnCleared -= OnWaveCleared;
CurrentWave.OnEnemySpawned -= OnEnemySpawned;
CurrentWave.OnEnemyKilled -= OnEnemyKilled;
}
if ( !ClearedLastWave )
{
currentWaveIndex = ( currentWaveIndex + 1 ) % waves.Length;
CurrentWave.Reset();
CurrentWave.OnCleared += OnWaveCleared;
CurrentWave.OnEnemySpawned += OnEnemySpawned;
CurrentWave.OnEnemyKilled += OnEnemyKilled;
}
}
/// <summary>
/// Called when the given wave has been cleared (all the instantiated enemies have been destroyed)
/// </summary>
private void OnWaveCleared( Wave wave )
{
if ( ClearedLastWave )
{
Debug.Log( "You have cleared the last wave, congratulations!" );
}
else
{
Debug.Log( "Wave Completed! Next one starting in " + timeBetweenWaves + " seconds" );
}
waveCountdown = timeBetweenWaves;
spawningWave = false;
waveCompletedPanel.SetActive( true );
waveCompletedText.text = wave.Name + " Completed";
StartCoroutine( HideWavePanel( 2 ) );
}
private void OnEnemySpawned( Wave wave, Transform spawnedEnemy )
{
waveEnemiesCountText.text = string.Format( "{0} / {1}", wave.RemainingEnemiesAlive, wave.Count );
}
private void OnEnemyKilled( Wave wave, Transform killedEnemy )
{
waveEnemiesCountText.text = string.Format( "{0} / {1}", wave.RemainingEnemiesAlive, wave.Count );
}
/// <summary>
/// Hides the wave panel after the given delay
/// </summary>
private IEnumerator HideWavePanel( float delay )
{
yield return new WaitForSeconds( delay );
waveCompletedPanel.SetActive( false );
}
}
Original answer
// Enemy script
public event System.Action OnDestroyed;
private void OnDestroy()
{
if( OnDestroyed != null )
OnDestroy();
}
// Spawner script
// Top of the file
using System.Collections.Generic;
// ....
private List<Enemy> enemies = new List<Enemy>();
private void SpawnWave()
{
// ...
Enemy enemy = Instantiate( enemyPrefab ).GetComponent<Enemy>() ;
enemies.Add( enemy ) ;
enemy.OnDestroyed += () => OnEnemyDestroyed( enemy );
}
private void OnEnemyDestroyed( Enemy enemy )
{
enemies.Remove( enemy ) ;
Debug.Log( "An enemy has been destroyed. " + enemies.Count + " left");
}
@OnEd0t I've reworked your Wave
and WaveSpawner
script, and extracted Wave
to its own file for better clarity.
Feel free to ask questions if you don't understand everything.
Duoooode!....YOU ARE Such A Legend! thank you so much for the help, honstly didnt expect help as much as you gave me. thank you just thank you
but i have a couple of questions
1- now i have to fill my hierarchy with waves and refrence them in the waveSpawner script in the Waves class, right?
2-after all of this work you still havent told me how to make the counter for the enemies
anything i do for you to return this favor, but dont ask for points i have non, this is my first time asking on this website, again thank you
Yes, you have to fill the information in the inspector of the
WaveSpawner
component. Wasn't it like this before?Yes, I haven't added the real counter, I will edit my answer to give you a clear way to get it
Answer by suman818 · Jul 14, 2019 at 08:30 AM
you need to update your list every time you kill an enemy. just run the forEach loop after each time you kill the enemy.
Answer by Dawdlebird · Jul 14, 2019 at 08:56 AM
How about just having a public static int called "enemyCount" or something? You could put that on your Spawner script or a game manager script. Then when you instantiate enemies just up that variable (like "Spawner.enemyCount++" for example), and in your onDestroy function you add the line "Spawner.enemyCount--" This way each enemy is responsible for being accounted for themselves. This saves you the cpu overhead over finding gameobjects by tag or having to do forEach loops or anything of the sort. Also having a single integer is more memory friendly than tracking a dynamic list.
Your answer

Follow this Question
Related Questions
Resouce script help 3 Answers
GuiText Score On Mouse Click 1 Answer
Roguelike Elements... 1 Answer
Counter problem 1 Answer
Connecting 2 scripts(javascpt)? 2 Answers