[Unity] Generic class inheritance problem notes in singleton

Unity Advanced Study Notes: Message Framework_Unity Message Framework_Raine_Yang's Blog-CSDN Blog

In the past few days, when I was studying the news framework of this big guy, I also encountered the problems he encountered, and came up with different insights and solutions. The whole framework is roughly as follows:

Message Center (MessageCenter Class) -> Function Manager (ManagerBase Class) -> Function Script (MonoBase Class)

MessageCenter class: use List to manage each ManagerBase class, perform message broadcasting, and find the manager corresponding to the function.

ManagerBase class: Use List to manage each MonoBase class, broadcast messages, and find scripts corresponding to functions.

MonoBase class: used to implement specific functions and receive message broadcasts.

It is necessary to write management classes with specific functions (such as PlayerManager and AttributeManager), which inherit the ManagerBase class; and scripts with specific functions (such as PlayerController and PlayerAttribute), which inherit the MonoBase class.

And for the MessageCenter class and the manager class of each specific function, it needs to be singletoned to ensure that each class has only one instance and provide it with a global access point.

The singleton template script is as follows:

using UnityEngine;

public class SingletonBase<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance;
    public static T Instance
    {
        get
        {
            return instance;
        }
    }
    protected virtual void Awake()
    {
        instance = this as T;
    }

    protected virtual void OnDestroy()
    {
        instance = null;
    }

}

For the MessagerCenter class, you can perform the following singleton operations, then create an empty game object in the scene, and mount the script on it:

public class MessageCenter : SingletonBase<MessageCenter>
{
    public List<ManagerBase> Managers = new List<ManagerBase>();

    public void Register(ManagerBase manager)
    {
        if(!Managers.Contains(manager))
        {
            Managers.Add(manager);
        }
    }

    public void SendCustomMessage(Message message)
    {
        foreach(var manager in Managers)
        {
            manager.ReceiveMessage(message);
        }
    }
}

But for the ManagerBase class, if the following similar singleton operations are performed:

public abstract class ManagerBase : SingletonBase<ManagerBase>
{
    public List<MonoBase> Monos = new List<MonoBase>();

    public void Register(MonoBase mono)
    {
        if (!Monos.Contains(mono))
        {
            Monos.Add(mono);
        }
    }

    public virtual void ReceiveMessage(Message message)
    {
        if (message.Type != GetMessageType())
        {
            return;
        }
        foreach (var mono in Monos)
        {
            mono.ReceiveMessage(message);
        }
    }

    public abstract byte GetMessageType();
}

It will be found that when different function management classes inherit the ManagerBase class, no matter how many function management classes are written, they all have only one common instance (one of all function management classes).

The reason is that no matter how many function management classes are written, they will be instantiated as ManagerBase class (because the ManagerBase class specifies that the type of singleton is ManagerBase class, not a specific subclass), resulting in all function management inherited from ManagerBase class Classes have only one instance in common (one of the subclasses).

When I want to use a generic class to write a set of singleton methods (as described by the blogger above),

Define the ManagerBase<T> class, inherit MonoBehaviour and specify T to inherit from MonoBehaviour,

At the same time, when the List type of MessageCenter is changed to List<ManagerBase<MonoBehaviou>>,

I found that if I define the PlayerManager class and inherit the ManagerBase<PlayerManager> class, I will find that ManagerBase<PlayerManager> cannot be implicitly converted into ManagerBase<MonoBehaviou>, which will lead to the inability to add this management class to the List of MessageCenter.

The reason is that although PlayerManager is derived from MonoBehaviour, ManagerBase<PlayerManager> and ManagerBase<MonoBehaviou> cannot be equal .

So I thought of a solution. The MessagerCenter class remains the same, write a ManagerBase class for storage in MessagerCenter, and then write a ManagerBase<T> class, which inherits from ManagerBase, and realizes singleton in this class :

using System.Collections.Generic;
using UnityEngine;

public abstract class ManagerBase : MonoBehaviour
{
    public List<MonoBase> Monos = new List<MonoBase>();

    public void Register(MonoBase mono)
    {
        if (!Monos.Contains(mono))
        {
            Monos.Add(mono);
        }
    }

    public virtual void ReceiveMessage(Message message)
    {
        if (message.Type != GetMessageType())
        {
            return;
        }
        foreach (var mono in Monos)
        {
            mono.ReceiveMessage(message);
        }
    }

    public abstract byte GetMessageType();
}

public abstract class ManagerBase<T> : ManagerBase where T : MonoBehaviour
{
    protected static T instance;
    public static T Instance
    {
        get
        {
            return instance;
        }
    }

    protected void Awake()
    {
        instance = this as T;
    }

    protected void OnDestroy()
    {
        instance = null;
    }
}

Later, it can be found that in the corresponding function management class, different singleton objects can be stored in MessageCenter normally, and the problem has been solved so far.

using System.Collections.Generic;
using UnityEngine;

public class PlayerManager : ManagerBase<PlayerManager>
{
    private void Start()
    {
        MessageCenter.Instance.Register(this);
    }

    public override byte GetMessageType()
    {
        return MessageType.Type_Player;
    }
}

Guess you like

Origin blog.csdn.net/weixin_64080879/article/details/131849824