Search code examples
c#classunity-game-engineeventsdelegates

Can't store a method from another class inside a delegate in another class when that clas is instantiating


I have a method in a class that I want to add as a listener to a button that I'm gonna instance soon.

I'm using a PopUpManager that queues PopUp classes and instantiates them as they go and in a PopUp class, I have a "Continue Button". I am able to add the "Show Next Pop Up" method as a listener to it from Pop Up Manager Class. What I want is be able to pass a method to PopUpManager (btw it's singleton) so in PopUpManager I can add that method as a listener to the "Continue Button".

The way I tried is having a delegate inside PopUp class and before adding PopUp to a queue, I am filling that delegate with the method I want to call. I can store the method in a delegate yes and I am debugging and seeing it but after instantiating the object delegate reference becomes null. Every data I filled before is stays but delegate is becoming null. What am I missing?

public class PopUpPair
{
    private PopUp popUp;
    private object data;

    public PopUp PopUp => popUp;
    public object Data => data;

    public void Create<T>(PopUp key, T value) where T : class
    {
        popUp = key;
        data = value;
    }
}

Here is the PopUpManager:

public void AddPopUpToQueue<T>(PopUp popUp, T data, PopUp.ContinueCallBack callBack)
{
    var _pair = new PopUpPair();
    popUp.SetCallBackAction(callBack); // This fills the variable and I can log it before instantiating the object.
    _pair.Create<object>(popUp, data);
    popUpQueue.Enqueue(_pair);
}

public void ShowNextPopUp()
{
    if (popUpQueue.Count == 0) return;

    var _popUpToSpawn = popUpQueue.Dequeue();
    var _popUp = Instantiate(_popUpToSpawn.PopUp, transform);
    _popUp.CallBack += ShowNextPopUp; // This works fine.
    _popUp.CallBack += _popUp.CallBackAction; // Here is the null reference from CallBackAction.

    if (_popUpToSpawn.Data != null)
        _popUp.FillData(_popUpToSpawn.Data);
}

Inside PopUp:

public delegate void ContinueCallBack();
public event ContinueCallBack CallBack;

private ContinueCallBack callBackAction;
public ContinueCallBack CallBackAction => callBackAction;

I can see the method's name here so data variable is valid.

public void SetCallBackAction(ContinueCallBack action)
{
    callBackAction = action;
    Debug.Log(callBackAction.Method.Name);
}

Also when I try this inside PopUp class, CallBack only has one listener from the PopUpManager with "ShowNextPopUp":

public void SetCallBackAction(ContinueCallBack action)
{
    CallBack += action;        
}

Solution

  • That's because delegate type is not serializable in Unity, here are the rules: Serialization Rules. The instantiated popup no longer keeps the original callback reference, but you can still refer to the original one:

    _popUp.CallBack += _popUpToSpawn.CallBackAction;
    

    If you need a serializable callback type, you can use UnityEvent instead:

    public UnityEvent callBackAction;