Justin Lloyd
Justin Lloyd

Reputation: 123

Unity3D: Custom UnityEvent AddListener not firing

I have my own custom UnityEvent and am trying to add a listener.
I have used AddListener on numerous other UI objects, such as buttons, dropdowns, toggles, etc. so I understand the process. However, when I Invoke my UnityEvent, it simply doesn't fire. I'm receiving no error messages, and after doing reading and research, everything looks correct. So, not sure what to do further.

This is an object that emits when it's rotated.

This is the basics of my code:

using UnityEngine.Events;

public class Rotator: MonoBehaviour 
{
    public UnityEvent OnRotate;
    int angle = 0;
    int newAngle = 0;

    void Start() 
    {
        OnRotate = new UnityEvent();
    }

    void Update() 
    {
        newAngle = (int)transform.rotation.eulersAngles.z;

        if (newAngle != angle) 
        {
            print ("Actual Instance ID: " + GetInstanceID());
            print ("Invoking!");
            OnRotate.Invoke();
            angle = newAngle;
        }
    }
}

and

public class Owner: MonoBehaviour 
{
    public Rotator rotator;

    void Start() 
    {
        print ("Rotator Instance ID: " + rotator.GetInstanceID());
        rotator.OnRotate.AddListener(
            () => UpdateRotation()
        );
    }

    void UpdateRotation() 
    {
        print ("Invoked!");
    }
}

When the Rotator has it's angle changed, I get this in the console:

Actual Instance ID: 11234
Rotator Instance ID: 11234
Invoking!

The instance ID is to make sure I'm working with the same objects and not going in circles for nothing. They match, so I'm listening to the object that's firing. However, the listener isn't firing. I've tried different combinations with delegates, etc. but it's all the same. No errors. It just doesn't invoke.

Obviously, I'm doing something wrong, but what is it?

Thanks for any help.

Upvotes: 1

Views: 6680

Answers (2)

derHugo
derHugo

Reputation: 90580

Somehow your answered your new edited version of the question with exactly the code you previously provided in the First Version of your Question!

As I tried to tell you ... if you anywhere in your code do OnRotate = new UnityEvent() of course you thereby erase any persistent callbacks and any runtime callbacks added before that moment!


In short

Simply leave it as

public UnityEvent OnRotate;

and you don't even have to think about it anymore.


For understanding why it also works if you put it in Awake please simply have a look at the Order of Execution for Event Functions

→ First Awake and OnEnabled is called for every GameObject/Component. Then all Start methods are called as soon as the GameObject/Component is active.

Within each of these blocks (Awake + OnEnable) and (Start) the order of execution between different component types is not guaranteed unless you explicitly configure it via the Script Execution Order Settings where you could define that Owner is simply run before Rotator .. then having both in Start would also work again.


Why does it also work if you do it on the public field?

→ Because this field is serialized. That means it is initialized automatically in the Inspector and then stored together with the Scene or prefab asset including any persistent callbacks.

And then Later Unity re-uses the serialized Version of the field so actually you can completely remove the new UnityEvent(); since it doesn't have any effect on a serialized field! It will always be initialized automatically anyway!

Upvotes: 5

Justin Lloyd
Justin Lloyd

Reputation: 123

Ok, I found out what the issue was. My question now is "why?".

I changed my code from:


public UnityEvent OnRotate;

void Start() {
  OnRotate = new UnityEvent();
}

to

public UnityEvent OnRotate = new UnityEvent();

void Start() {
}

And now it works.

Although, now that I think about it, Awake() is the method where they all fire before initialization, whereas Start() is when the object is created. So the Start() of the Rotator is probably getting called after the Owner is adding a listener.

Upvotes: 0

Related Questions