How to handle enemy dying inside trigger collider?
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.
Answer by graciasluigi · Sep 22, 2015 at 12:01 PM
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
}
}
Thanks for the answer @garcialuigi ! Certainly given me a lot to go on. I wasn't aware of delegates until now and have spent the day reading up on them.. or at least trying, From what I've read, it's the best way forward - but I really cant get my head around the concept.
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. public Action Got$$anonymous$$illed;
I'd be really, really grateful if you could elaborate on this.
To help put the example in context, I'm building a clone of the old TPS Desert Strike game. I have a player with a PlayerTargeting script (which manages the list of targets based on Ontrigger enter and exit events). And, I have enemies, which have EnemyHealth (has public TakeDamage method) and EnemyHUD (manages health bar per enemy) scripts attached.
How would the delegate technique fit in here?
Thanks again, Dave
Updated comment
Hi @garcialuigi. This is great - thanks for the explanation.
So, I've been away and studied a lot more about delegates. although it's still not 100% clear in my head, I'm a lot closer. I've also implemented your example code in my game but I cant figure out what 'enemyTriggered' is, in;
enemyTriggered.Got$$anonymous$$illed += EnemyGot$$anonymous$$illed;
I've tried other.gameobject.Got$$anonymous$$illed += EnemyGot$$anonymous$$illed but it doesn't like that either.
What object does Got$$anonymous$$illed belong to in my trigger events??
Thanks again!
Answer by orchard800 · Sep 25, 2015 at 09:09 PM
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
Awesome dude, looks like you got it. :) $$anonymous$$akes more sense the OnDeath delegate field being static as you did, though.
Thank you so much. This worked perfect for my little tower defense game.
Your answer
Follow this Question
Related Questions
How to make an object move to the direction in which the user faces using vr gaze interaction? 1 Answer
Triggers colliding with triggers 1 Answer
What do i do if i have problems with tags? 1 Answer
Calling "OnTriggerEnter" when a parent object has a rigidbody 0 Answers
Trigger collider change fog color 0 Answers