Search Unity

UltEvents - The Ultimate Event System For The Ultimate Price - FREE

Discussion in 'Assets and Asset Store' started by Kybernetik, Jun 1, 2019.

  1. Froggy_AI

    Froggy_AI

    Joined:
    May 25, 2020
    Posts:
    1
    Hi, I'm using UltEvent in my project, everything is good~ but I found I must using getter/setter to change properties
    For example, I have a Script
    Code (CSharp):
    1.  
    2. public class Tool_TransformFollow : MonoBehaviour
    3. {
    4.     public Transform Target;
    5.  
    6.     public bool FollowPos { get; set; }
    7.  
    8.     public bool FollowRot;
    9.  
    10.     // Update is called once per frame
    11.     private void LateUpdate()
    12.     {
    13.         if (!FollowPos && !FollowRot)
    14.             return;
    15.         if (FollowPos)
    16.         {
    17.             transform.position = Target.position;
    18.         }
    19.         if (FollowRot)
    20.         {
    21.             transform.rotation = Target.rotation;
    22.         }
    23.     }
    24. }
    But when I use UltEvent to access this component, it only shows FollowPos
    upload_2020-6-5_10-33-20.png

    Is there something I misunderstand?
     
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    UltEvents can only call methods. That includes properties because they are actually just methods internally. But
    FollowRot
    is not a property, it's a field. Adding support for fields would have a performance cost for everyone even if they don't use that feature so I decided against it.
     
    Dawn31 likes this.
  3. AlejMC

    AlejMC

    Joined:
    Oct 15, 2013
    Posts:
    149
    Wanted to point out an issue, although maybe it's not a bug a the way unity behaves.
    The fact that UltEvents can call static classe's properties and methods is amazing, however, does it work only with project's internal static ones and not Unity's ones like the ones on UnityEngine? (this differs per-platform).

    Description:
    - Tried to wrap quickly a 'quality settings' menu by putting some buttons with UltEvent callbacks when the buttons are clicked.
    - If I call the QualitySettings static class methods and properties like SetQualityLevel or .maxTextureLimit = 2, it works in editor, PC and macOS. However it doesn't work on iOS.
    - If I call properties/methods of a custom made class that then forwards the call to the QualitySettings ones, then it works on all platforms.

    Maybe on iOS those classes get obfuscated? signature changed or something else?

    Just curious and wanted to point that out.

    EDIT:
    Xcode output logs basically says that couldn't Invoke a method because it's not found.
     
  4. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Check your script stripping level in the player settings. Unity could be removing that method because none of your actual code is using it. Unfortunately, I don't think there's a way to tell it not to strip a specific method so using a custom class to call what you need is probably the way to go.
     
    AlejMC likes this.
  5. AlejMC

    AlejMC

    Joined:
    Oct 15, 2013
    Posts:
    149
    I had never ever thought about nor knew about exactly what stripping levels could mean on a practical level. So probably if I made an IL2CPP Windows build something similar would happen on that platform too.
    Will give this a try as soon as I can for gathering more learning info about this.
     
  6. Jim_Zee_King

    Jim_Zee_King

    Joined:
    Mar 10, 2014
    Posts:
    10
    Hello, i just stumbled on this nice asset and i wanna thank you for it!

    I have this question though :

    is it possible to have a function called that takes a string as parameter but instead of showing a input field in the inspector, it would show a popop from an array of string so that i can choose the string to use as argument from that popup list.

    Thank you again!
     
  7. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    It would probably be possible to add a feature like that, but it would take quite a bit of effort and I'm not really adding new features to it at this point. If I do end up doing more work on it, there are quite a few other features that would take priority over something like this.
     
  8. Jim_Zee_King

    Jim_Zee_King

    Joined:
    Mar 10, 2014
    Posts:
    10
    Thank you for the feedback, i might try to add in the source code provided!
    Have a nice one!
     
  9. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Highly appreciate the section on performance!!
    Why do you check null if you do that in the get{}?
    upload_2021-1-28_18-16-47.png
     
  10. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Because most events are unlikely to be used. You might put that script on an object just to receive the Exit events while not caring about Enter or Stay events so having it create new event instances that never get used would just waste time and require more garbage collection when that component is destroyed.
     
    laurentlavigne likes this.
  11. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    regarding the display, is there an option to remove the "->" and void which add visual noise

    also is there a way to flatten this menu branching like the unity event?

    upload_2021-1-29_14-14-56.png

    much easier to read:
    upload_2021-1-29_14-15-19.png
     
  12. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    There isn't a setting for it, but you could easily open the MethodSelectionMenu script and remove the usage of "->".

    To make it not show a void return type, you can go to the BuildAndCacheSignature method in that script and have it check
    if (returnType != typeof(void))
    before appending the return type.

    Disabling the sub menu groupings can be done with the Display Options at the top of the menu.
     
    laurentlavigne likes this.
  13. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Great, thanks!
    How would I remove the UnityEngine and combine base with gameobject?
    upload_2021-1-29_21-42-6.png
     
  14. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    If there aren't display options for it, any modifications to that menu will need to be done in the MethodSelectionMenu script.
     
    laurentlavigne likes this.
  15. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    alright, thanks, i found that there is a sub menu in the display menu that flattens things.
    ULT is quite an upgrade in term of usability! In your doc you mention that perfs on 1 param is slightly lower than uEvent, is it so when an event is raised or as a perf hit over every frame even when no event is raised?
     
  16. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Only the cost of invoking the event. Events have no CPU cost while they aren't being used.
     
  17. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Very cool, I've never used them on a cpu starved device before so never cared for perfs. Seems that it should do fine for all GUI.
     
  18. Quills

    Quills

    Joined:
    Dec 10, 2012
    Posts:
    5
    Hello,
    I've been using this asset for a while and previously made some minor modifications/extensions (to add simple conditional aborts to PersistantCall).
    Recently I've been using UltEvents more often and ran into the following QoL issue:

    How could I go about using a (previously) returned script as the Target for a future PersistantCall? In the same way you can use returned values as inputs to parameters of future persistant calls?

    Currently I'm using the below setup.
    - Get a script returned value from first PersistantCall
    - Check its not null with the previously mentioned simple static conditional abort method
    - Feed that value into a static method as a third PersistantCall to get a value/call a method on that script
    It works but its a bit clunky and requires writing extra extra static methods to access each property/method on the returned script.
    upload_2021-2-1_10-55-16.png


    Ideally I'd like to be able to do the below setup instead, andpick a method/parameter to return from the type provided in the inspector.
    upload_2021-2-1_10-57-9.png

    If you've got any suggestions for how to implement this I'd be very interested to hear them (or if there's an existing way to do this that I've overlooked)!

    Thanks for your time
     
  19. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    There isn't an existing way to do it and I haven't been able to do any development on UltEvents recently, but I have thought of a few ways it could be done.

    The simplest would probably be to just give each PersistentCall an extra PersistentArgument which gets used as the method target if the _Target is null. That probably wouldn't be too hard to implement, but would be pretty inefficient.

    Another possibility would be to just put that extra argument in the array with the others and mark it as special. If the call's _Target is null, check the first argument and if its _X is NaN then use that argument to get the target from its linked value.

    That's probably the approach I'd go for if I tried to add it to the current system, but if I do end up working on it again there's a good chance that I might decide to completely redo the entire serialization system. Instead of giving each event an array of calls which each have an array of arguments, I could just give each event one array of each core type (int, float, string, object) and handle the serialization/deserialization of calls myself. That would be a lot of work, but it would give a lot of flexibility and make features like this trivial to add.
     
    Quills likes this.
  20. Quills

    Quills

    Joined:
    Dec 10, 2012
    Posts:
    5
    Hey, thanks for your fast reply and suggestions!
    Would you be able to expand on the explanation of the second of your suggested ways of doing this though? I don't really understand the parts in bold.

     
  21. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Having the first argument's type as ReturnValue or Parameter wouldn't necessarily mean that argument is intended to be the method's target so you need another flag to indicate when that's the case. Since the _Int is already used to store the index of the linked value, the _X field would be the next most efficient to use as the flag so you can just set it to float.NaN if it's meant to be the method's target.
     
    Quills likes this.
  22. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Is there a way to access a monobehavior I just want to turn moveController.enable=false/true
    Code (CSharp):
    1.     MonoBehaviour moveController => (MonoBehaviour) Player.me.thirdPerson;
    moveController is not showing
    View attachment 787784

    Also to access TMP text field in ultEvent I need to go through 5 submenus at the moment which is not practical so I was wondering if there is a setting to make it as easily accessible as UnityEvent.
    View attachment 788717
     
  23. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The enabled property should be under the Base Types sub menu.

    Avoiding sub menus would need a whole new menu system or search system or something, but I don't know of a simple solution. UnityEvents only get away with it because they don't support as many methods (no privates, less parameter types, only one parameter, no return values) so their menu is much more empty, but as you can see that last menu looks like it fills the screen on its own.

    My Inspector Gadgets plugin adds context menu functions to Copy and Paste all Inspector fields which can let you copy entire events or individual calls if you're setting the same thing up frequently, but that's not really a solution to the problem.
     
    laurentlavigne likes this.
  24. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Is there a way to know which fields are exposed in the inspector and filter out everything else?
     
  25. imgodot

    imgodot

    Joined:
    Nov 29, 2013
    Posts:
    212
    Kyb,

    I am setting up a system where I use a persistent listener as a kind of template for creating a dynamic listener at runtime.

    In the editor, I create a persistent listener where the target object is a prefab.
    This is what I call the "template".

    What I want to do is:
    At runtime, when the prefab is instantiated, read the persistent listener and use its properties to add a dynamic listener that is identical to the persistent listener except that its target object is the instance of the prefab in the hierarchy, instead of the prefab object in the project.

    I can do this with some of the pieces hard-coded, but now I am stuck.

    I can get the persistent call like this ...
    Code (CSharp):
    1. UltEvents.PersistentCall call = EvtManager.MyTestEvent.PersistentCallsList[0];
    Note: "EvtManager" is a monobehavior with an event called "MyTestEvent" defined as a public property.

    This works but I want the code to be able to access new events I define on "EvtManager" without having to specify the "MyTestEvent" directly.

    Do you have an idea of how I might accomplish this?

    Thanks.
    -- Paul
     
  26. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    @laurentlavigne I've never seen a utility method to check it so you'd need to implement the logic yourself based on the Serialization Rules.

    @imgodot You could put all the events in a list or use a coroutine like:
    Code (CSharp):
    1. // in EvtManager
    2. public IEnumerator<UltEvent> GetEnumerator()
    3. {
    4.     yield return MyTestEvent;
    5.     yield return SomeOtherEvent;
    6.     ...
    7. }
    8.  
    9. // Then iterate through them with a foreach loop
    10. foreach (UltEvent ultEvent in EvtManager)
    11. {
    12.     // Do stuff with the ultEvent.
    13. }
    If you want to avoid needing to manually add every event to the GetEnumerator method, you'd need to use reflection to iterate through all fields and gather the events in a list which you can then iterate through.
     
    laurentlavigne likes this.
  27. imgodot

    imgodot

    Joined:
    Nov 29, 2013
    Posts:
    212
    Kyb,

    Thanks for the reply.
    I am currently iterating through all public fields on EvtManager as FieldInfo objects.
    When I find a FieldInfo with my custom attribute, I would like to manipulate the event it represents.

    However, I don't know how to get a reference to the event from the FieldInfo object.
    Code (CSharp):
    1. UltEvent tempEvtRef = (UltEvent)myFieldInfo... [magic happens here]
    Thoughts, my master?
    -- Paul


    NEVER MIND. I GOT THAT PART WORKING. (edited to add this)
     
    Last edited: Feb 5, 2021
  28. imgodot

    imgodot

    Joined:
    Nov 29, 2013
    Posts:
    212
    Kyb,

    My code works reading a persistent call and generating a dynamic call, however, it still requires the event type to be hard-coded.

    In the snippet below, I get an event reference ("tempEvt") from the FieldInfo object then get the persistent call info using that event ref. This code relies on a hard-coded event type ("UltEvent tempEvt") and if I want to process a different event that has parameters, it requires a different event type reference, like: "UltEvents.UltEvent<string> tempEvtWithString;"

    Do you know if there is a way to generalize this code so I can get ANY type of event into a single event reference?
    Is there an interface I can use?
    Some other possibility?

    I tried "UltEventBase", but "AddDynamicCall()" doesn't like being passed an EventBase.
    TRIED: UltEvent.AddDynamicCall(ref eventAsEventBase, action);

    Thanks.
    -- Paul

    Code (CSharp):
    1. UltEvent tempEvt = (UltEvent)myFieldInfo.GetValue(evtManager);
    2.  
    3.             UltEvents.PersistentCall call = tempEvt.PersistentCallsList[0];
    EDIT: Added the "EventBase" stuff.
     
    Last edited: Feb 5, 2021
  29. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The easiest solution is probably to just make UltEventBase.DynamicCallsBase public.
     
  30. imgodot

    imgodot

    Joined:
    Nov 29, 2013
    Posts:
    212
    Kyb,

    So, I changed all the "protected" references of DynamicCallsBase to "public"; in the attached image.

    With them as public AddDynamicCalls still complains that it can't convert from event base to "UltEvents.UltEvent"

    Thoughts on where I went wrong?

    Thanks.
    -- Paul
     

    Attached Files:

  31. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    With DynamicCallsBase public, you should be able to do what you want by accessing that property directly.

    Or you could make another AddDynamicCall method that takes an UltEventBase instead of UltEvent.
     
  32. imgodot

    imgodot

    Joined:
    Nov 29, 2013
    Posts:
    212
    Kyb,

    Yeah, sorry.

    I don't see any properties or methods of DynamicCallsBase that will let me do what I need.
    Or, if I saw them, I didn't recognize how to use them.

    I also tried adding my own AddDynamicCall() method that accepts an UltEventBase argument but then I'm still stuck because "e.DynamicCalls ..." within my new method does not accept an event base argument; the same problem I had before.

    Thanks for your help.

    Code (CSharp):
    1. public static void AddDynamicCall(ref UltEventBase e, Action method) {
    2.             if (e == null)
    3.                 e = new UltEvent();
    4.  
    5.             e.DynamicCalls += method;
    6.         }
    7.  
     
  33. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I was thinking you'd be able to add the method to DynamicCallsBase, but that's not going to work. For an UltEvent the dynamic delegate needs to be an Action (a method with no parameters) and for UltEvent<T> it needs to be Action<T> (a method with one parameter), which means that you're never going to be able to assign the same method to both types of events.

    The easiest thing to do at this point would probably be to just give UltEventBase an Action field for ParameterlessDynamicCalls and have the Invoke method in each event type call that delegate as well.
     
  34. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    I am trying to reference a LevelPin_SO scriptable object inside another scriptable object
    upload_2021-2-16_21-0-56.png
    This scriptable object looks like this inside:
    Code (CSharp):
    1. public class LevelPin_DATAPIPE : ScriptableObject
    2. {
    3.     public LevelPin_SO levelPinSo;
    4. }
    Code (CSharp):
    1. public class LevelPin_SO : ScriptableObject
    2. {
    3.     public string name;
    4.     public bool unlocked;
    Is this possible in UltEvent?
     
  35. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    UltEvents can't access fields, only call methods (including the getter or setter of a property). So you can just make a property to wrap the field you want to access:
    Code (CSharp):
    1. public string Name
    2. {
    3.     get => name;
    4.     set => name = value;
    5. }
     
    laurentlavigne likes this.
  36. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Thank you. That was easy.
    Is there a way to make it work with FormerlySerializeAs so I don't break all my inspector connections when I rename properties classes or methods? Or alternatively an attribute that raises hell when I try refactoring at 3am?
    upload_2021-2-17_9-54-31.png
    upload_2021-2-17_9-54-37.png
     
  37. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Unfortunately not, I would need to create my own attribute and manually check for it which means that if it ever fails to find the target method it needs to go through every other method in that class to check for the attribute and would be quite inefficient.
     
    laurentlavigne likes this.
  38. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    and what about this, ref is not recognized by ult, is there a way around this?
    upload_2021-2-18_15-52-48.png

    EDIT: when I press the [?] it shows
    upload_2021-2-18_15-57-27.png
    I'm expecting another bucket to place another reference, how do you use it?
     
  39. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Support for ref parameters is on my list of things to consider if I do any further development, but it's not something that can easily be done with the current system.

    For now, you'd just have to make a second method with a non-ref parameter which calls the ref one.
     
    laurentlavigne likes this.
  40. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Gotcha, I'll just do this DataOut MethodName(Params) and it works fine.
    upload_2021-2-18_19-14-40.png
    (I see an infinity icon on the second line, great visual helper, the first line could have an arrow if you ever fiddle with it)

    EDIT: wow! ult is such a time saver
    upload_2021-2-18_20-12-2.png
     
    Last edited: Feb 19, 2021
  41. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I can't see anything wrong with that method.

    What do the definitions of LevelPin_SO and LevelPin_SO.TYPE look like?
     
    laurentlavigne likes this.
  42. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    It works, I had a... bah never mind the reason, user error.
     
  43. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    When an ultevent has a missing reference it'll spit out these.
    the problem is I'm using tons of ult events and I don't know which gameobject or class called it
    what do I need to add to this to spit out the name of the GO?
    upload_2021-3-11_20-54-30.png
     
  44. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Method calls don't necessarily need to come from a GameObject or any object at all, but I guess you could change it to throw an exception so that clicking on it in the Console window should highlight the object in the Hierarchy.
     
  45. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    how do i do that? I don't see any variable that returns which mono called it.
     
  46. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    There isn't one that you can access, but if you just throw an exception then Unity's exception handling system should automatically link the resulting console message with the calling object if there is one.
     
    laurentlavigne likes this.
  47. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    I'm getting these errors in ultimate event system
    upload_2021-3-29_18-21-18.png
    it happens there
    upload_2021-3-29_18-21-26.png
    that second one causes the error, it's just a gameobject from the scene
    any other object works fine
     
  48. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    found it: there was a missing script on that one, maybe add a catcher
     
  49. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    On the line with
    c.GetType() == type
    just change it to
    c != null && c.GetType() == type
    .
     
    laurentlavigne likes this.
  50. PartyBoat

    PartyBoat

    Joined:
    Oct 21, 2012
    Posts:
    97
    @Kybernetik Does UltEvents support disabling domain reload to allow for Unity's fast enter play mode? If not would you consider adding support? Personally, for all my new projects I have been pruning any assets that don't support this just because it's such a massive productivity boost.

    Bonus Questions: Same as above but for Animancer Pro. I will regrettably move back to mecanim if not but I'd really love to stick with Animancer having seen what it's capable of.