How a child gameObject know when its parent got destroyed.

Hello,

I am making a Particle System Pool: I have a list of 10 fire-effect.

Everytime a monster got burn, I will place the [fire-effect-gameObject] into the [Monster-gameObject], and call Play(), so the fire will begin and stick with the monster when it move.

After the fire effect stop, i use the ParticleSystem onStop callback event to move out itself from the Monster, and come back to the pool for later use.

But there was a problem, during the fire effect happen, if the monster dead and destroy(), the fire effect, is destroyed as well, because at this time it is the child of the monster. And later, although my code have a variable hold the ref to it, I cant use it anymore (null reference).

So, my question is: how my gameObject get a event from Unity as soon as its parent destroyed, so I can move it out, and prevent it from destroy with it parent?

Thank you.

When your monster “dies” just have it check if it has a particle on it and then send that particle back before it destroys itself.

Also to note, you don’t necessarily have to destroy your monster. You can pool those as well depending on if you are spawning the same monsters or not.

2 Likes

Yes, but this will make dependency problem.
If I put this fire-effect into any gameObject (monster, player…). I must write code in Monster, player… to return it into the pool.
Are there any way that my fire-fx itself can detect that it will be destroy in next frame? (something like onWillBeDestroyed(), called by Unity). So I will not worry, I can put it anywhere, and it will come back to pool.

Why are you not just instantiating it and destroying it? Because you want to use 10 different fire fx?

1 Like

Because everytime instantiate, the framerate will drop. And I have hundreds of other effect like poison, freeze, slow… thats why I need a pool to re-use them.

Well, I’m not aware of an event, but you got me curious about something, so I tried it out.

When destroy is called on a gameobject, 2 Unity methods are called. The first is OnDisable and the second is OnDestroy. Now, if you wait till OnDestroy, you’re too late, it’s already on it’s way out. But, you can trigger something in OnDisable on the child. So, simply add an OnDisable method to your fire fx and have it return on when that triggers.

8411721--1111653--upload_2022-9-2_21-3-25.png
The order of execution should allow for the child to return home before the parent destroys itself.
Note I only tested this with 1 parent and 1 child, you might need to test with more.

1 Like

Very simple. You make a script on your parent that reacts to OnDestroy and sends a cue to each of its children. Children react to this by detaching themselves from the parent.

Parent script

void OnDestroy() {
  var receivers = GetComponentsInChildren<ParentListener>();
  foreach(var item in receivers) item.Notify(destroyed: true);
}

Script on your children

public class ParentListener : MonoBehaviour {

  public void Notify(bool unwanted, bool destroyed, bool smellingFlowers) {
    if(destroyed) transform.SetParent(null);
  }

}
1 Like

Thank you very much. This may help me a lot ;)!

Thank you. This is the first way I had think about. But as I said on post #3, this will create dependency. Thats mean, my FX-Pool will depend on monsters, players, or anything that I will put my fx into. I am looking for a way that I can put my fx in any gameObject, and make sure it always return, without prepare any code in parent gameObject :slight_smile:

Also, when OnDestroy() was call, it too late to save the children. It will be destroy at this time.

You’re probably right, but OnDisable would suffice for this example as Brathnann suggested. But even better, you should be the owner of the cycle, by introducing Kill method to your parent script. Finally, you can set a delay for a Destroy.

void Kill() {
  var receivers = GetComponentsInChildren<ParentListener>();
  foreach(var item in receivers) item.Notify(destroyed: true);
  Destroy(gameObject, 0.3f);
}

As for the pooling, I have to think about what your implications are.
Here you said:

I don’t see how this adversely affects anything. Do you mean like you don’t want to have to Destroy your objects? But you don’t have to, that’s just an example.

If you have a child script

public class ParentListener : MonoBehaviour {

 public void Notify(bool unwanted, bool destroyed, bool smellingFlowers) {
    if(destroyed) {
      transform.SetParent(null); // detach from parent
      Destroy(this); // will remove this script but not the object
      // or it might simply trigger a pooler or whatever
    }
  }

}

There are a lot of ways to make this happen, you don’t have to just destroy the object. The key point I’m trying to make (and probably others too) is signaling. If you can signal between objects, what else is required?

1 Like

@orionsyndrome yes, I got what you mean.

Monster: Hey all fx attach to me, I will die next frame, you guy should get back to your pool.
Fx: ok, we will do.

Player: Hey all fx attach to me, I will die next frame, you guy should get back to your pool.
Fx: ok, we will do.

Flying Arrow: Hey all fx attach to me, I will die next frame, you guy should get back to your pool.
Fx: ok, we will do.

by this way Monster, Player, Arrow… must keep a list of all fx that attach to it.
Everytime Fx attach to monster:
Fx: Hey monster, I am fire, and I will attach to you. Keep a ref on me and let me know before you die!
Monster: Ok I will put you on my list.

==========
I think you know what I need to achieve is:
Fx: Attach to monster silently. Monster do not know what fx attach to him.
Fx: keep looking at monster, and when it see the monster die. It will leave him, silently.
and thanks Brathnann for onDisable() event, so Fx do not need to implement update to check the monster everyframe.

I dont know which way is better, but for me, it’s more complex if monster, player, arrow… or anything we put fx on, must have a list of receiver to notify. We must check when to add, remove, clears those receiver :frowning:

You don’t need to keep a list of receivers.
First you know the receivers are among the children, second you can use a tag for your fx objects.

Then you can implement this

public HashSet<GameObject> GetChildrenByTag(string tag) {
  var hset = new HashSet<GameObject>();
  for(int i = 0; i < transform.childCount; i++) {
    var go = transform.GetChild(i).gameObject;
    if(go.CompareTag(tag)) hset.Add(go);
  }
  return hset;
}

(This works ok if you don’t have dozens or more of children.)
Now you can use this set to do whatever

var hset = GetChildrenByTag("fx");
foreach(var childObject in hset) {
  var listener = childObject.GetComponent<Listener>();
  if(listener != null) listener.Notify(...);
}

GetComponentsInChildren would also work if X components was something that only lived on your fx objects.
You can also make it so that the message itself declares the intended audience, so that only valid listeners respond etc.

You can also make your own version of GetComponentsInChildren so that it supports tags as well.

public Component[] GetComponentsInChildren<Listener>(string tag) {
  var list = new List<Component>(this.GetComponentsInChildren<Listener>());
  for(int i = list.Count - 1; i >= 0; i--) {
    if(!list[i].gameObject.CompareTag(tag)) list.RemoveAt(i);
  }
  return list.ToArray();
}

Of course, this should be a generic method so that all kinds of components are supported

public Component[] GetComponentsInChildren<T>(string tag, bool activeOnly = false) where T : Component {
  var list = new List<Component>(this.GetComponentsInChildren<T>());
  for(int i = list.Count - 1; i >= 0; i--) {
    if(!activeOnly || list[i].gameObject.activeSelf) {
      if(!list[i].gameObject.CompareTag(tag)) list.RemoveAt(i);
    }
  }
  return list.ToArray();
}

There are literally hundreds of ways to do this, many of which are simple, but the best one depends on your exact needs and I cannot possibly list them all.

If there were many children objects, for example, this kind of naive approach wouldn’t be optimal any more, and I’d personally implement a decent event notification system, to which the objects would subscribe when spawned, and unsubscribe once they’re gone.

Then you can broadcast stuff into the “air” so to speak, and the intended audience would react by some internal logic. No physical connections between the objects are required. That way even if you had hundreds of children, just one or two of them would actually actively receive the message, because the rest of them didn’t even need to subscribe to anything.

And so on. Events are btw natively supported by C# in a sense there is a high-level vocabulary to set it up, and it will fill in the boilerplate for you (and the common logic, for example keeping the list of all subscribers is something you don’t have to do, you just “add” a subscriber to a declared event and the magic happens).

1 Like