Is there a way to make an UnityEvent have a return value?

I want to be able to choose a condition in the inspector, just like a delegate (i’m not using delegates because they don’t show up in the inspector).
Something like this would be perfect:

public UnityEvent someEvent; // Event is choosen in inspector

public void Start()
{
    int someVariable = someEvent.Invoke(); // Doesn't needs to be an int especifically
}

That’s not really how events work.

Consider if you attached multiple listeners to said UnityEvent… which one’s result would you be returning?

I guess you could say the last one attached (which is how delegates work in C#). But nope, that’s not how UnityEvent works, they just forego that oddity all together.

Events as a design are really just a way for one object to say “I did something”. So other objects can react to that thing it had done. The alerting object isn’t really supposed to change its behaviour based on who happened to listen to it. It creates a really weird dependency with a loose link between them.

If you need that sort of dependency. Instead just have a direct reference to that listener. Just have a field of the type of the listener, and attach it directly. Then call the method of importance on it.

4 Likes

Yep, in general, though I’m reminded that I do have a specific class in my General assembly that functions as a priority queue version of an event, and the listeners can consume the event and keep the remainder of the queue from being called afterward. That’s not returning a value to the event invoker, but to the event handler itself though (and not returning anything so much as just changing a value in the eventargs).

If you need a value sent back to the invoker, you should pass in a callback delegate as a parameter to the invoke method. This enables all listeners to “return a value” to wherever the invoker specifies, but it doesn’t demand it the way a return type does.

1 Like

if you need to implement conditions, you can define an interface ICondition and classes implemented ICondition like below:

public interface ICondition{
   bool CheckCondition();
}
public class CCondition:Monobehaviour,ICondition{
   [SerializeField]
   float disTH;
  [SerializeField]
   Transform target;
   public bool CheckCondition(){
        float dis;
        //Compute the distance between the gameobject and the target
        return dis<disTH;
   }
}

Therefore you can call GetComponent().CheckCondition()
you can define an array of conditions with operators as well
Also you can write your own custom editor

For future googlers

I created an asset similar to UnityEvent but has return values. Usage is exactly the same as UnityEvent, and performance is similar as well. Comes with a default “Condition” that returns a bool. No interfaces or inheritance required.
3455254--273902--34294989-46de127e-e70b-11e7-84f0-99bc4525a8f5.png

Check out the github repo: GitHub - Siccity/SerializableCallback: UnityEvent and System.Func had a child

24 Likes

Man this is AMAZING, this should be part of unity.

3 Likes

Great decision, but I have problems after building my pc app. When I used yor asset, it didn’t launch and I found it was because of more than one instances of serialized class or classes, who’s parent is SerializableCallback<>, for example:

using UnityEngine;

public class Example : MonoBehaviour
{
    [System.Serializable]
    public class IntReturnIntEvent : SerializableCallback<int, int> { }

    public IntReturnIntEvent getPower;
    public IntReturnIntEvent getVelocity;
}

By the way, in Editor ewerything works great. Have any idea, how to fix it?

Hmm, I’ve never had a problem with it on mobile platforms, but I’m getting issues on pc build too. Not sure what’s causing it though. I’ll have a look at it sometime soon.

Is there any result in problem solving?:slight_smile:

No, sorry, i forgot all about it. And now i just tried and can’t reproduce it. Can you send your Unity version and possibly the error/line you’re getting?

Edit: Problem should be fixed by now

This looks awesome, and is similar to something I’ve started searching for in Unity. I wonder if you have any thoughts about it?

I’ve started wondering if there was a way to have an inspector UI that lets you choose specific VARIABLES on a target property. For example, say I wanted to build a bit of UI that was designed to display an arbitrary float variable, and intended to be cloned in multiple locations on a canvas to show different floats with identical presentation.

The dream is that I could take one of those UI components, and via an Inspector interface point it towards a float variable on some other component on some other object. That would let the UI be reusable without having to write custom script to reference specific source floats to drive it.

The SerializableCallback is the closest thing I’ve found, but works with methods. The dream version of this would use a similar interface to define a reference to variables.

Does anything like that exist already in Unity? Is there a conceivable path to having such a thing? This seems like it mighe be a breadcrumb on that path: Access variable by string name?

Thanks - SlapworthFH

Hi Siccity,
thanks four your work.
I downloaded SerializableCallback code from the link you provided and I followed the instructions to create a serializable condition. Like this:

[Serializable]
public class SerializableCondition : SerializableCallback<bool> { }

It works perfectly in Unity, but if I make a build and install it on an Android phone, the game crashes when it tries to load the prefab that contains a SerializableCondition member.

It’s pretty easy to reproduce the issue.

  1. Create a game object
  2. Create a MonoBehavior script having the previous SerializableCondition as a field
  3. Assign a boolean function to the SerializableCondition in the Inspector
  4. Make that game object a prefab
  5. Create an instance of that prefab when loading your Main Scene…and Boom. Game crashes.

So if everyone is interested, be aware that at the moment it does not work on a mobile device.

You could create an event object that gets passed around and modify its properties via your event handlers/subscribers, and then it could get read by your invoker for changed values.

myObj modifiableObj = new myObj();
// allow eventHandlers to modify the object
someEvent.Invoke(modifiableObj);
int i = modifiableObj.publicProp;

// define custom Unity Event
public class MyEvent : UnityEvent<myObj>
{ }

// in the eventHandler which you choose from the inspector
public void OnSomeEvent(myObj modifiableObj)
{
    modifiableObj.publicProp = 1; // or whatever int you need
}

Although probably not the cleanest way, since you still run into the problem if you have multiple event handlers for the event.

For a slightly different use case, a simple C# property can be hooked up to a UnityEvent.

Declare, for example, a float UnityEvent. That allows to use the setter aspect of a property assigned as event target.

public class UnityEventFloat : UnityEvent<float> { }
public UnityEventFloat floatEvent;

But how to also access the getter of the target ?

Solution:

System.Reflection.PropertyInfo targetProperty = null;
object target = valueCallback.GetPersistentTarget(0); // returns Monobehaviour
string method = valueCallback.GetPersistentMethodName(0);
if (target!=null && method!= null)
{
  targetProperty = target.GetType().GetProperty(method.Replace("set_",""));

   if (targetProperty != null)
     float value = (float) targetProperty.GetValue(target);
}

EDIT: Added call to method.Replace (“set_”,“”)

2 Likes

I am SO glad I spent all day searching for this. I think you just changed my life. THANK YOU!

1 Like

Thank you! Now I can finally decouple components properly. Also, thanks for making it a package dependency, very easy to add to a project.

The callback is not showing in Editor. Whats the wrong i did?

Bump with a solution that doesn’t involve reflection or anything nasty.

 [Serializable]
public class GetDepth : UnityEvent<GameObject, Action<int>> { }


//Here is where the unity event would trigger the method (probably in a different script! same name error in same script):
public void GetDepth(GameObject target, Action<int> depthCallback)
{
     var host = FindNode(target.transform);

     if (host.parent == null)
     {
         depthCallback(0); //this is the callback
     }
     else
     {
         depthCallback(host.depth); //this is the callback
     }
}


//finally this is where you want the callback value when used:
void Blah() {
int depth = 0;
GetDepthOfTreeObject.Invoke(m.gameObject, (val) => { depth = val; });

//depth will be set appropriately after it is invoked (assuming the event actually ran the action)
}
2 Likes

That’s a clever way to avoid reflection. Points for that, but I would probably still use this approach with a custom type and property drawer like Siccity did that only supports a single callback. An event can have any number of observers, so you can get any number of return callbacks invoked in response to firing an event. I see a single return value from a single callback as generally more useful. However, there are cases where having all observers return a callback response could be useful.

Also, you created a closure with the lambda sent out with the event invocation. That’s going to have bad performance and allocate extra garbage memory every time you invoke the event. It would be better to just explicitly create a method, probably a private method so you know it’s always in response to your event invocation. Then, explicitly cache an Action instance pointing to that method, stored as a member of the class that is invoking the event. Then pass the cached Action instance to the event so it’s not generating a new one and creating garbage every time the event fires.

I think your example could also be simplified to not include any of that GameObject target, node, host, parent, depth stuff so that the important idea is more clearly conveyed. All of the other stuff seems really specific to something you were working on and makes the important idea less clear.

Ahh, but now that I’m thinking out loud, I realize that creating a custom type and property drawer is where people turn to reflection and custom serialization.

Maybe you could make a custom type that still uses UnityEvent under the hood for it’s nice editor integration and serialization capabilities, but with a custom property drawer that limits to one observer. Of course, custom property drawers don’t 100% guarantee that multiple observers will never be assigned. I suppose you could just set a member bool before invoking the event, then toggle it back in response to the first return callback, and after the event in case of no observers. Then only the first responder is considered valid, but it still seems a bit unclean. I like anything simple that avoids performance costs though.

It would be neat if Unity would just integrate a type like UnityEvent that only accepts a single observer so you could attach a Func with a return value.