Search Unity

Events - Is this suitable?

Discussion in 'Scripting' started by Reloque, Jul 24, 2017.

  1. Reloque

    Reloque

    Joined:
    Apr 28, 2015
    Posts:
    207
    I am struggling a bit to grasp the intent of the event system. Here is my case;
    • I have a GUI, that display number of beasts in a scene via some icons
    • I have mobs, that run around in a scene
    • Mobs can be added by the player
    • Mobs can die, if that happens a few functions need to be called, sound, internal administration, etc.
    So, game runs, a mob dies, I have written a function that handles that and I want to be able to call that via an event. Call the event, and the GUI get's updated, the number of mobs active becomes one lower, room for one mob is created etc. I have created the event "MobDies" via

    Code (csharp):
    1. public UnityEvent MobDies;
    in a seperate CS file "MobEvents.cs". That script is then attached to an empty object in the scene. And in the inspector I dragged the functions that need to happen once the event triggers does into the event. When I now do

    Code (csharp):
    1. MobDies.Invoke();
    in MobEvents.cs, it works perfectly.

    But, how do I trigger that event outside of MobEvents.cs? Every mob for instance has a MobController.cs that contains a Die function, I would like to put something like
    Code (csharp):
    1.  Event.Trigger("MobDies");
    or the like in there, so trigger a globale event, like a mouseclick. But how do I do that? Is that the right way to go about that? Or should I add a pointer to every Mob that I instantiate so it can invoke the events that way?
     
  2. GameDevJon

    GameDevJon

    Joined:
    Jul 21, 2017
    Posts:
    25
    For something like this, you don't need to use the Unity Event System.

    Check out Delegates & Events. Essentially the same thing, but this will allow you to raise events that can updae the GUI when a mob dies.
     
  3. Reloque

    Reloque

    Joined:
    Apr 28, 2015
    Posts:
    207
    Not just the GUI. Also a whole load of other stuff. It hooks into how many mobs can be summoned, wether or not certain enemies knows etc. Second, the GUI is not the a GUI, but something slightly more complex.

    Also, the GUI Example is just one. I have other cases that require the same. Basically, a mob dying or doing other stuff should tell everyone it's dead.
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    You can add things to the UnityEvent when you spawn the thing:

    Code (csharp):
    1. //in your spawner:
    2. var spawnedMob = Instantiate(mob); //or however you create the mob
    3.  
    4. var deathEvents = spawnedMob.GetComponent<MobEvents>().MobDies;
    5.  
    6. deathEvents.AddListener(Handle_Death_Somehow);
    Now the MobDies event will both do whatever you assigned in the inspector, and Handle_Death_Somehow. If you need Handle_Death_Somehow to know what has died, use a lambda:

    Code (csharp):
    1. deathEvents.AddListener(() => { Handle_Death_Somehow(spawnedMob) });
     
    Reloque likes this.
  5. Reloque

    Reloque

    Joined:
    Apr 28, 2015
    Posts:
    207
    I don't think I understand completely. What I want is an event to trigger once a mob dies. The contents of that event are already defined, ie, update GUI, update some administration, etc. But, that dying can occur on multiple spots and as so, I want to be able to trigger that even from multiple spots. I already know how to react to an event, I need to figure out how to trigger the event.

    So, my question then becomes "how do I trigger an event?"

    I can do it in the MobEvents.cs by using "Invoke", but I want to be able to do it in other spots as well, without having to give each instantiated Mob a reference to the MobEvents script.
     
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    You want a global messaging system? So your mobs can fire off "MOB_DIED" without having an object to call that method on?


    It seems like you want MobEvents to be a singleton, and then expose a static method on that to invoke the method. So in MobEvents:

    Code (csharp):
    1. private static MobEvents instance;
    2.  
    3. void Awake() {
    4.     instance = this;
    5. }
    6.  
    7. public static void FireMobDies() {
    8.     instance.MobDies.Invoke();
    9. }
    Would that be suitable? The alternative is to give the MobEvents object to each mob as they're spawned.
     
  7. Reloque

    Reloque

    Joined:
    Apr 28, 2015
    Posts:
    207
    I am really doubting now. It's a design and model choice I guess. Learning project, so thanks for the help. This is the context;

    The player can summon and dismiss minions, ie mobs. Those mobs can also die in various ways. Right now, everything is done in playerController and when a GUI element is clicked, a boolean in the playerController, summonMinion, is set to true. In the Update call that boolean is checked, if true, it start a summon animation and instantiates a minion object. Also, it adds that object to a list, so the playerController has a reference to all summoned minions.

    If the GUI command to dismiss a minion is received, it works kind of the same way. A boolean, Kill minion is set. In Update that boolean is checked, if true, a animation is started to kill the minion. And once it's gone, it's removed from the list.

    If another thing, say a fall from a cliff, kills a minion then the minion also should die, be destroyed and be removed from the list. Right now, I was just referencing back from the Minion back to the playerController. But all that cross-referencing was starting to feel really clunky. Player has reference to a MinionList, each minion has to have a reference to the Player and all those checks make the Update function seem quite unorganised. As such, I was looking for a better way. Both ways you suggest might work. But in this case, what would be the elegant design choice?

    upload_2017-7-27_13-50-39.png

    It's the above schematic, but this way, everything is handled by the playerController, the GUI, the administration etc. All objects in the scene just have a reference to playerController and set booleans in the playerController to get things done. I feel if, for instance, a minion falls of a cliff, that shouldn't involve the playerController. Movement, actions etc. should be done there. But, right now, like I said, it's clunky.
     
    Last edited: Jul 27, 2017
  8. Reloque

    Reloque

    Joined:
    Apr 28, 2015
    Posts:
    207
    To rehash, what would be the most elegant way to administrate such a scene. Using a singleton to administrate the summoning, disposal and gui of the summoned minions seems a likely choice. Don't know if it's the best one.
     
  9. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    From this:

    It seems like you've already got an Update loop that iterates over the minions and controls them. There's no reason destruction/removal shouldn't be handled there:

    Code (csharp):
    1. void Update() {
    2.     for(int i = minions.Count - 1; i --; i >= 0) {
    3.         if(KillMinionSet(minions[i])
    4.             PlayDeathAnim(minions[i]);
    5.         if(Dead(minions[i]) {
    6.             Destroy(minions[i].gameObject);
    7.             minions.RemoveAt(i);
    8.         }
    9.     }
    10. }
    You'd generally use events to avoid having update loops like that. If you already have a loop like that, there's no reason to pay the cognitive overhead of events.
     
  10. Reloque

    Reloque

    Joined:
    Apr 28, 2015
    Posts:
    207
    Putting it that way, no. The minions can easily use the singleton to signal their status. Save me the use of of events indeed. Thanks.