I’ve created a simple target management system that keeps a list of enemies inside the player’s trigger collider. The first item in the list is the current target and the target lock is placed in this gameobject… and so on.
The problem is when an enemy dies inside the trigger collider. It doesn’t produce an OnTriggerExit event and so never leaves the target’s list. So how do I tell my player that that enemy no longer exists?
Any advice greatly appreciated.
I’m not sure why I cant get the above working… Anyway, I’ve put the effort into really learning about events and delegates, by watching and reading as many different tutorials as possible. All thanks to @garcialuigi pointing me in the right direction. To finally understand it now (at least at a fundamental level) is hugely rewarding and feels great!
So, for anyone following and looking to do similar, here’s the trimmed final code that is tested and working.
// EnemyHealth.cs with unrelated stuff removed
public class EnemyHealth : MonoBehaviour
{
// Set up a delegate called onDeathAction which passes on the enemy GameObject.
// ie tell it which enemy has died
public delegate void OnDeathAction(GameObject enemy);
// Set up the event called OnDeath to be published, using the delegate OnDeathAction
public static event OnDeathAction OnDeath;
...
public void TakeDamage(int amount)
{
...
if (currentHealth <= 0)
{
// if OnDeath has subscribers.
// ie functions in other classes registered to listen for the event
if(OnDeath != null)
{
// Announce enemy death to subscibers
OnDeath(this.gameObject);
}
...
}
}
}
and
// PlayerTargeting.cs with unrelated stuff removed
public class PlayerTargeting : MonoBehaviour {
// set up variable for new list
public List<GameObject> acquiredTargets;
// set up variable for selected target
public GameObject selectedTarget;
void Awake ()
{
// create new targets list
acquiredTargets = new List<GameObject>();
}
void OnTriggerEnter(Collider col)
{
//if collision tagged as enemy and target is not already in targets list
if (col.tag == "Enemy" && !acquiredTargets.Contains (col.gameObject))
{
// add target to targets list
acquiredTargets.Add (col.gameObject);
// Register EnemyDied function with OnDeath event in EnemyHealth
// ie Do EnemyDied() when OnDeath event occurs
EnemyHealth.OnDeath += EnemyDied;
// Refresh Targets list
// makes sure selected target = acquiredtargetslist[0]
UpdateSelectedTarget();
}
}
void OnTriggerExit(Collider col)
{
//if collision tagged as enemy and target is not already in targets list
if (col.tag == "Enemy" && acquiredTargets.Contains (col.gameObject))
{
// remove target from list
acquiredTargets.Remove (col.gameObject);
// Unregister EnemyDied function with OnDeath event in EnemyHealth
// ie Don't do EnemyDied() when OnDeath event occurs
EnemyHealth.OnDeath -= EnemyDied;
// Refresh Targets list
UpdateSelectedTarget();
}
}
public void EnemyDied(GameObject enemy) /* Note the same parameters as the delegate */
{
// if dead target is in list
if(acquiredTargets.Contains(enemy))
{
// Remove dead target from targets list
acquiredTargets.Remove(enemy);
}
// Refresh Targets list
UpdateSelectedTarget();
}
}
Hope this helps someone!
Thanks,
Dave
Seems right IMO that the enemy doesn’t exited the trigger, he is still there, doesn’t matter that he is dead.
I think you need to tell your player that the enemy is dead in another way.
- With a flag, state;
- You can predict that the enemy will die when attacking it;
- You can register your player to a callback(delegate) in the enemy, or in some manager. When the enemy dies, it dispatch this delegate, then the player will know that a specific enemy just died.
Lets try one example, could be something like this.
But I believe that there is a lot of ways you could do what you want.
You can have a central manager script, where enemies call a method to tell that they died.
Etc.
Delegate is a function pointer. Awesome to work with, dependency injection, lambda expressions, functional programming. I recommend you learning it, might not be the best option for now, but of course you will need it in other cases. 
public class EnemyHealth
{
// Action<T> is a built in C# generic delegate
public Action<EnemyHealth> GotKilled;
public void TakeDamage()
{
...
if(damageKilled)
{
isDead = true;
// If there is someone registered in the Action/delegate
if(GotKilled != null)
{
// Tell that I died
GotKilled(this);
}
}
}
}
public class PlayerTargeting
{
private void OnTriggerEnter(Collider other)
{
...
// Register
enemyTriggered.GotKilled += EnemyGotKilled;
...
}
private void OnTriggerExit(Collider other)
{
...
// Unregister
enemyTriggered.GotKilled -= EnemyGotKilled;
...
}
private void EnemyGotKilled(EnemyHealth enemyHealthKilled)
{
// Remove from list, etc
}
}