Search Unity

UnityEvent, where have you been all my life?

Discussion in 'Scripting' started by JoeStrout, Apr 24, 2015.

?

Did you know about UnityEvent before this?

  1. No! Thank you! This is so cool!

    36.2%
  2. No, but I still don't see what's the big deal.

    5.1%
  3. Yes, but I didn't realize how cool they were.

    24.1%
  4. Yes, but I still don't see what's the big deal.

    12.2%
  5. Yes, and I use them all the time. Where have you been?!?

    22.4%
  1. Deleted User

    Deleted User

    Guest

    Let me know your new thread so I can follow it. :)
     
    lermy3d likes this.
  2. ashley

    ashley

    Joined:
    Nov 5, 2011
    Posts:
    84
    Hmm I think I've got it but always good to check! Bear with me, I'm fairly new to all this still. I can supply scripts if it helps, but for now here's the basics:

    I have a Money script which has two functions - add money and subtract money.

    In my enemy script I've got a raycast that sets a bool to true when it spots the player. It also has an event that is invoked when the bool is true, which then runs the subtract money function from the Money script.

    Is that...right? It works, but I know with these kind of things you can accidentally make things work sometimes even when you do it badly! But is the logic/flow correct? Or should the enemy script invoke the EventTrigger script I have, which in turn invokes the subtract money method? I'm not sure if this is a duplication of work or not. In my head it would be 'neater' to have all things going through the ET script, but I suppose you would need to invoke something from the enemy script in the first place so it's an impossible ambition?

    I also need to figure out why it continually subtracts money when in sight but I think that's a maths thing. I'll try and figure that one out myself!
     
  3. lermy3d

    lermy3d

    Joined:
    Mar 16, 2014
    Posts:
    101
    I just created a new thread for the custom event system.

    I also posted a new version where I implemented all the sorting improvements. The reflection update method is now called only one time, when the event method becomes unavailable, the target component has changed or Unity's Editor is done compiling. So we can now type at the speed of light! haha! :)

    Have a nice one.
    Lermy
     
  4. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Sounds reasonable to me. Be sure to distinguish between "every frame when this bool is true" and "I just now changed this bool from false to true." Those should be different events. Or maybe you don't need both of them, but just be clear which one you have.

    But yeah, you've got the right idea; your scripts are completely decoupled into one that only knows about money, and doesn't care who or why is telling it to shift money around; and another one that only spots the player, and doesn't care what happens when the player is spotted. Neither script knows or cares about the other script. And then, only in the scene editor, you hook these together so that spotting the player causes money to decrement... and could also cause a sound to play, some other behavior to engage, the sprite to change, or whatever.

    Sounds to me like you're well on the road to enlightenment... keep up the good work!
     
    ashley likes this.
  5. ashley

    ashley

    Joined:
    Nov 5, 2011
    Posts:
    84
    "Be sure to distinguish between "every frame when this bool is true" and "I just now changed this bool from false to true.""

    Hmm I think my brain is too fried to figure that out today! But I've made the kind of progress I was looking to make today and getting my head around events more :)

    Although trying to figure out sequential/combined events (i.e. if this has happened and this has happened run this event). That's for tomorrow...hopefully!

    Thanks again!
     
    lermy3d likes this.
  6. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Well that's good. Sorry if I was unclear. I just meant, decide whether you want to send the event on every frame:

    Code (CSharp):
    1.   bool flag;
    2.   public UnityEvent onFlagTrue;
    3.  
    4.   void Update() {
    5.     if (flag) onFlagTrue.Invoke();
    6.   }
    ...or, only invoke the event when the state changes:

    Code (CSharp):
    1.   bool _flag;
    2.   public UnityEvent onFlagSetToTrue;
    3.  
    4.   bool flag {
    5.     get { return _flag; }
    6.     set {
    7.       if (value && !_flag) onFlagSetToTrue.Invoke();
    8.       _flag = flag;
    9.     }
    10.   }
    Usually, you want the latter... sending events on every frame seems a bit heavy-handed to me.

    Best,
    - Joe
     
  7. Dan2013

    Dan2013

    Joined:
    May 24, 2013
    Posts:
    200
    @JoeStrout
    Very nice thread. Thanks!
    Did someone do some comprehensive benchmark about the UnityEvent?
    I mean some tests only for UnityEvent, not comparing something else.
    For example, if I let GameObject A invoke 100~1000 events per frame, and let GameObject B receive these 100~1000 events per frame. What would happen? Is that possible some events are backlogged for a very short time? Does each event generate 0 byte memory garbage in GC?
    Or, testing measurements I listed above are not comprehensive enough. Any other measurement I should include?
     
    JoeStrout likes this.
  8. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    That's really not the kind of load or use case that an event system is intended for. If you know that A has to make many calls to B then A should almost certainly have a reference to B.

    Event systems are for when things should be decoupled or, potentially, completely unaware of one another. They tend to be called relatively infrequently - only when things change, and generally from objects where changes occur less than once per frame.
     
    JoeStrout and Kiwasi like this.
  9. Dan2013

    Dan2013

    Joined:
    May 24, 2013
    Posts:
    200
    OK. Maybe that is an over simplified example.
    A better example will be a complex messaging system, where GameObjects depend each others based on triggering UnityEvents. During a busy frame, EventA is invoked, which triggers EventB1, EventB2 in handler of EventA. Then, EventB1 and EventB2 triggers other more Events in a similar way. This chaining triggers repeatedly happen in this busy frame. Can UnityEvent be used in this kind of scenario?
    I may do a test later.
     
    lermy3d likes this.
  10. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    You'd have to have a huge chain for performance to matter.

    That aside, there's potential issues with UnityEvent chaining if the any method gets called multiple times in the chain.

    Edit: Correction, any method, not just the original one.
     
    Last edited: Nov 19, 2015
    lermy3d likes this.
  11. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I'm now using UnityEvent all over the place in my projects, as the primary decoupling mechanism. I have a collection of components that trigger events when various things happen (scene loading, key press, etc.), other components that do generally useful things from public methods that can be invoked as events (cross fading, interpolating from one transform state to another, etc.), and some components specifically designed to relay events after a delay, or with some cooldown period, or whatever.

    I snap these suckers together like LEGOs.

    So, anecdotally, I can say that chains of events work like a champ in my projects and have never caused a performance problem.

    But I don't believe I've ever introduced an actual loop, as in the situation @angrypenguin describes. So that may be something to watch out for.
     
    angrypenguin and Deleted User like this.
  12. Deleted User

    Deleted User

    Guest

    Ever since I started working WITH Unity rather than against it, and using its component architecture and UnityEvents, I've made a lot more progress and have had a much easier time designing components. I haven't tested performance yet myself, but I use UnityEvents a lot as well. Well, each component has 1-2 UnityEvents but my components are quite small.
     
    Last edited by a moderator: Nov 20, 2015
    angrypenguin and JoeStrout like this.
  13. lermy3d

    lermy3d

    Joined:
    Mar 16, 2014
    Posts:
    101
    In the case of the project I am working on the most useful part of the UnityEvents comes with the fact that I can save them into prefabs along with GameObjects, in that way I can detach the UI logic from the client application code, since most of my GUI assets are in a bundle.

    In that way I can change the entire game GUI and the Logic attached to it on the server, for instance I can setup a thematic interface for Christmas along with a new window saying 'Happy Christmas' to the users and no new version of the client application is required.
     
    Last edited: Nov 19, 2015
    the_motionblur likes this.
  14. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    @lermy3d, that sounds really useful, and is magic I didn't know about. Do you have a blog post, tutorial, or manual page somewhere that explains it?

    EDIT: Found it.
     
    Last edited: Nov 19, 2015
    the_motionblur and lermy3d like this.
  15. lermy3d

    lermy3d

    Joined:
    Mar 16, 2014
    Posts:
    101
    I haven't made an article about it so maybe you are right and I should, is a really good suggestion since I perhaps took that for granted when all the bundle system was incorporated on Unity.
     
  16. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Yeah, you totally should. It's an incredibly powerful trick, and I'd love to see how you're applying it in real life. And, to make it relevant to this thread, be sure to include how you're using UnityEvents to incorporate new functionality without needing new code. :)
     
    lermy3d likes this.
  17. lermy3d

    lermy3d

    Joined:
    Mar 16, 2014
    Posts:
    101
    It has being a long road to get the system stable on the side of Unity, since a lot of issues existed in the first versions of the asset bundle system, like referencing scripts and adding scenes to the bundles. But right now everything is working pretty well so far.
     
  18. lermy3d

    lermy3d

    Joined:
    Mar 16, 2014
    Posts:
    101
    Okey man, let you know when I get the hands on that article and have it done!! In the mean time if you have any question you can ask me without problem.
     
    hopeful and JoeStrout like this.
  19. Dan2013

    Dan2013

    Joined:
    May 24, 2013
    Posts:
    200
    Oh... this issue seems a serious one...
    Ideally, event delivering should work just like message sending. When an event is delivered (like a message is sent to receiver), it is done. The later things like triggered handler or handler chain should not be related to the original event anymore, which results robust logic. Re-triggering the same event in the later handler chain should be OK.

    @angrypenguin
    Would you provide us the simplest snippet to reproduce the issue?
    If the issue does exists, we can submit a report to Unity. I think it is a serious issue, or some serious limitation that every UnityEvent user should be aware of.
     
    Deleted User and lermy3d like this.
  20. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    I sent my original repro case to Unity in a bug report, though I didn't have time to simplify it.

    And when using delegates rather than UnityEvent it was in fact 100% fine. My first thought was that I had screwed up my logic, but simply changing from UnityEvent to a delegate made it work perfectly.
     
    Deleted User, lermy3d and Dan2013 like this.
  21. Dan2013

    Dan2013

    Joined:
    May 24, 2013
    Posts:
    200
    Let's see how Unity guys reply to that report. :)
     
    lermy3d likes this.
  22. lermy3d

    lermy3d

    Joined:
    Mar 16, 2014
    Posts:
    101
    Hi angrypenguin, could you please post the link to the bug thread if is public to keep track of Unity's response, thanks a lot.
     
    Deleted User likes this.
  23. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    I'll do that when I get a chance, yes.
     
    lermy3d likes this.
  24. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    I'm absolutely positive I made it public, but I can't find the report listed and don't seem to have an email acknowledging that it was successfully lodged. It could be that it didn't upload properly for whatever reason. I might have to report it again.
     
    Last edited: Nov 22, 2015
    lermy3d likes this.
  25. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Ahh, found it, case 740481. Open and public, but hasn't made it to the issue tracker yet.

    Possibly because the attached project is... rather large? It might also be dependent on more than just the factors I identified, as just switching back to UnityEvents in my current version of the project didn't cause the same issue (but switching between UnityEvents and delegates in that project definitely caused it for me at the time).

    Edit: Actually, my repro case is definitely execution order dependent. If the GUI callback happens to get called first then it'll work fine. If the Objective callback happens to get called first then it'll cause the event to get raised again, which seems to kill the original invocation list and thus my GUI callback never gets hit.

    I might have to write a minimal case where it's going to miss something either way and submit that.
     
    Last edited: Nov 22, 2015
  26. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    That would be really helpful. I used to work on the engineering team at REALbasic (another cross-platform development environment, similar to Unity but focused more on business software). When we would get a bug report with only a huge project showing something weird that we couldn't quickly reproduce in a small project, 95% of the time it wasn't our bug at all, but something weird the reporter was doing that they (and we) didn't understand.

    Not saying that's the case here, but only that it's much more likely to get an engineer's attention if you can pare it down to a clear, bare-bones example with nothing else going on.
     
    Dan2013 and Deleted User like this.
  27. Dan2013

    Dan2013

    Joined:
    May 24, 2013
    Posts:
    200
    @JoeStrout @angrypenguin

    I agree with this. That is why I said a minimal snippet to reproduce the bug is very helpful.
    With that snippet, we may even able to do the analysis in this thread.
     
    Deleted User and lermy3d like this.
  28. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Well, like I said...
    If I had time I'd already have done it. I'm sure I'm not the only one capable of writing it, either.
     
  29. Deleted User

    Deleted User

    Guest

    No one understands the bug like you do since you've personally experienced it. If it takes too long for you to reproduce, I'm sure the same could be said for other people.
     
  30. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    I found it while polishing for a trade show, and haven't been much less busy since. I'll get to it when I get to it. My point was that if people are genuinely interested in seeing it move faster then they don't have to wait on me.
     
    JoeStrout likes this.
  31. Deleted User

    Deleted User

    Guest

    I'm really curious about 1 thing: UnityEvents can have either persistent listeners (serializable listeners set-up in the Inspector) or non-persistent listeners (delegates called at runtime). I wonder if there is a performance difference between the two. i.e. if I have 1 UnityEvent with 2 persistent listeners, 1 one with 1 persistent and 1 non-persistent, and 1 with 2 non-persistent listeners, which one will have the best performance.
     
  32. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I wouldn't expect that to make any significant difference — which is to say, even if there is any difference, in a real app it would be so far down in the noise as to be inconsequential.
     
    angrypenguin likes this.
  33. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    Would be nice if the Inspector would also show non-persistent listeners (listeners added via code) in a read-only manner, for debugging purposes. Also, some check to ensure a listener doesn't get added twice (if done via code).
     
  34. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    I agree! If you make a feedback suggestion for this, I'll give it a vote.

    What about always using RemoveListener before AddListener? It's like the pattern for C# events:

    Code (csharp):
    1. myCsharpEvent -= NewEventHandler;
    2. myCsharpEvent += NewEventHandler;
     
    Deleted User likes this.
  35. Deleted User

    Deleted User

    Guest

    That's a really cool idea! It might be hard to implement because of the way PropertyDrawers and Unity Serialization work but I'll see if I can throw this in a future version of my system!

    I developed an event system similar to UnityEvent with a lot more functionality called Serializable Delegates. I plan on submitting it to the Asset Store tomorrow.

    It includes a reorderable call list, invoking of methods with multiple parameters, including enum parameters.

    Check this post on my website for more info: http://justanothergamestudio.com/products/serializable-delegates/
    (Yes, I know the website could use some love!)
     
  36. Deleted User

    Deleted User

    Guest

    Last edited by a moderator: Feb 9, 2016
    lermy3d, AnomalusUndrdog and TonyLi like this.
  37. Deleted User

    Deleted User

    Guest

    I finally released my asset! It's still waiting for Asset Store approval but in the meantime you can get it straight from my website: http://justanothergamestudio.com/developer-tools/serializable-delegates/
    By purchasing it from the website, you'll also always get the latest up-to-date version! (It usually takes 5-10 business days for an asset to be updated on the Asset Store.)

    If you have any questions, feel free to PM me or post them in the official thread!
     
    Last edited by a moderator: Feb 15, 2016
  38. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Since I'm a Creaky Old Man, I'm not a big fan of wiring things up in the Inspector, and I have a whole bunch of instanced but generally-self-managing objects that could benefit from delegation. What is the reason a scripted UnityEvent reference can't be treated as weak?

    I'm not so old that I enjoy doing things the hard way -- I wound up here looking for a magic bullet for weak delegate references so I don't have to write a zillion OnDestroy de-reference routines... so far this combined with this is still looking like my best bet.
     
  39. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Beats me. I agree it'd be much nicer if event listeners hooked up in code were also cleaned up automatically.
     
    lermy3d likes this.
  40. lermy3d

    lermy3d

    Joined:
    Mar 16, 2014
    Posts:
    101
    Hi guys! I just wanted to let you now that there is a new version of the Event Delegate System with a lot of nice new features, the system is also free by the way, and I am pretending to keep it that way.

    Enjoy and remember to leave your comment or suggestion! Cheers!
     
  41. paulomuggler

    paulomuggler

    Joined:
    Mar 18, 2014
    Posts:
    4
    JoeStrout, sir, you have changed my life. Period. Thank you so much for sharing this!

    Now all is left is to discover how to get events with custom method signatures so I can forward parameters dynamically directly from the inspector, in a similar fashion to what you can do with toggles and methods with one bool parameter, or sliders and methods with a float parameter.
     
    lermy3d likes this.
  42. hakankaraduman

    hakankaraduman

    Joined:
    Aug 27, 2012
    Posts:
    354
    Hi, I have a question

    I was able to use delegates like this

    Code (CSharp):
    1. public delegate void CustomDelegate(string s);
    2. public static event CustomDelegate OnCustom;
    Since the event is static, I can subscribe to it without getting a reference to the instance of the class. That enables me to subscrite to a event in a lot of gameobject with only one line of code.

    I tried same with the unityevents but it gives null reference exception error. Is the intended behavior of unityevents?
     
  43. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    No, I suspect you didn't do something correctly. Can you show the code?
     
    idurvesh likes this.
  44. Harinezumi

    Harinezumi

    Joined:
    Jan 7, 2013
    Posts:
    54
    Hi there,

    I have a question regarding renaming functions that are used in UnityEvents set up in the Editor.

    When you set up a public function in a UnityEvent, then rename that function you will get a missing reference. How do you handle this situation? Is there some fast/automatic way to know if a function is being used by a UnityEvent in the Editor?

    I tried with Find Missing References tool, but it doesn't handle this situation.
     
  45. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Hmmm...

    I'd probably leave a stub for the old method that just calls the new one, marked with the Obsolete attribute. Afterwards, perhaps, write an Editor function that uses reflection to search everything for a match for the old one and re-assign it to the new one? You could even make that generic and give it a GUI. Keep in mind it'd have to check every scene and prefab, though.
     
    Flipbookee, Kiwasi and Harinezumi like this.
  46. Harinezumi

    Harinezumi

    Joined:
    Jan 7, 2013
    Posts:
    54
    Thanks for the response. This is what I suspected, that I need to write a script for doing this :(

    If or when I have that, I'll share it in this thread.
     
    angrypenguin likes this.
  47. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Can you get away with [PreviouslySerialisedAs] or similar?
     
    angrypenguin and Dustin-Horne like this.
  48. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    That's what I was thinking as well. A simple marker interface, though if you had events in different classes with the same name that would get tricky.
     
  49. mrpmorris

    mrpmorris

    Joined:
    Dec 25, 2012
    Posts:
    50
    I don't like them. Having looked at a project that uses them very extensively I can say that they make it very difficult to know from where certain code is being executed. If you link up the events in code however it is very easy to do a "find in files" in the source code and find the references in seconds.
     
    Hypertectonic and MV10 like this.
  50. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I have seen the same thing from time to time. It's very easy for logic to disappear off few a half a dozen events without knowing where it's gone or if it will ever come back. Debugging of inspector event chains can be a pain.

    Still, like most techniques if you use it sparingly and with respect it can do an incredible job.