Как мне сохранить те же типы классов, которые имеют разные общие обозначения внутри списка?

#c# #unity3d #generics

#c# #unity3d #общие классы

Вопрос:

Я повозился с этим, и у меня есть ‘RespawnManager’, который я хочу использовать для управления моими несколькими классами ‘SpawnPoint’ с разными обобщениями, но в итоге это вынудило меня использовать обобщения для моего ‘RespawnManager’, который мне не нужен.

Допустим, у меня был SpawnPoint<T> класс, и я создал SpawnPoint<Enemy1> , SpawnPoint<Enemy2> и SpawnPoint<Enemy3> . Есть ли какой-либо способ, которым я могу создать список, который может просто управлять несколькими ‘точками возрождения’ любого общего?

Базовый класс:

 public abstract class SpawnPoint<T> : MonoBehaviour
{
    //how big the range of the spawn protection is   
    public int spawnProtectionRadius = 20;
    public bool Occupied { get; set; }

    public bool IsInSpawn(Transform target) 
    {
        Debug.Log((target.position - transform.position).magnitude);
        if ((target.position - transform.position).magnitude <= spawnProtectionRadius)
        {
            return true;
        }
        return false;
    }

    public abstract T Get();
}
  

Класс, который наследует это

 public class SeaMineSpawnPoint : SpawnPoint<Seamine>
{
    public override Seamine Get()
    {
        return SeaMineObjectPool.PoolInstance.Get();
    }

    private void Start()
    {
        RespawnManager<Seamine>.respawnManager.AddSpawn(this);
    }
}

  

Менеджер возрождения:

 public class RespawnManager<T> : MonoBehaviour where T : Component
{

    public static RespawnManager<T> respawnManager;

    [SerializeField]
    private List<Transform> playerList;

    [SerializeField]
    private List<SpawnPoint<T>> spawnpoints;

    private float respawnCounter;

    private void Awake()
    {
        respawnManager = this;
    }

    private void Start()
    {
        foreach (SpawnPoint<T> sp in spawnpoints)
        {
            Debug.Log(sp.transform.position);
        }
    }

    public void AddSpawn(SpawnPoint<T> spawnPoint)
    {
        spawnpoints.Add(spawnPoint);
    }

    public void RespawnSeaMines()
    {
        if (respawnCounter > 5)
        {
            respawnCounter = 0;
            foreach (SpawnPoint<T> sp in spawnpoints)
            {
                foreach (Transform playerT in playerList)
                {

                    if (sp.Occupied == false amp;amp; !sp.IsInSpawn(playerT))
                    {
                        Component ourGameObj = sp.Get();
                        ourGameObj.transform.position = sp.transform.position;
                        ourGameObj.gameObject.SetActive(true);
                        sp.Occupied = true;
                        return;
                    }
                }
            }
        }

    }

    private void Update()
    {
        respawnCounter  = Time.deltaTime;
        Debug.Log(respawnCounter);
        RespawnSeaMines();
    }

}
  

ObjectPool

 //Class that's used for object pooling of different types.
    //'T' must be a Unity component or it will error.
    public abstract class ObjectPool<T> : MonoBehaviour where T : Component
    {
        //An object with this specific component that we use to copy.
        [SerializeField]
        private T prefab;

        //Makes sure that only 1 coroutine runs at a time
        private bool coroutineIsRunning;


        //The singleton instance to our object pool.
        public static ObjectPool<T> PoolInstance { get; private set; }

        //A queue is used to organize plus activate and deactivate objects which
        //have this component.
        protected Queue<T> objects = new Queue<T>();

        private void Awake()
        {
            //Set the instance of this pool to this class instance. Only one of these can be set.
            if (PoolInstance != null)
            {
                throw new System.Exception("Singleton already exists. Cannot make another copy of this");
            }
            PoolInstance = this;
        }


        public T Get()
        {
            //If the queue happens to be empty, then add a brand new component.
            if (objects.Count == 0) AddObjects(1);

            //Returns the generic component and removes it from the queue.
            return objects.Dequeue();
        }


        public void ReturnToPool(T objectToReturn)
        {
            //Disables the game object that the T component is attached to.
            objectToReturn.gameObject.SetActive(false);

            //Stores the T component in the queue.
            objects.Enqueue(objectToReturn);
        }

        public void AddObjects(int count)
        {
            for (int i = 0; i < count; i  )
            {
                //Create a new copy of the prefab.
                //The prefab is a game object with the T component attached to it.
                T newObject = Instantiate(prefab);

                //Disable the game object.
                newObject.gameObject.SetActive(false);

                //Add the T component to the queue.
                //The T component is attached to the game object we created earlier.
                objects.Enqueue(newObject);
            }
        }

        public T GetWithDelay(int time)
        {
            T genericToReturn = null;
            if (!coroutineIsRunning)
            {
                coroutineIsRunning = true;
                StartCoroutine(GetCoroutine(time, genericToReturn));
            }
            return genericToReturn;
        }

        private IEnumerator GetCoroutine(int time, T generic)
        {
            float counter = 0;
            while (counter < time)
            {
                counter  = Time.deltaTime;
                yield return null;
            }
            generic = Get();
            generic.gameObject.SetActive(true);
            coroutineIsRunning = false;
        }
    }
  

Комментарии:

1. как вы собираетесь использовать разные точки возрождения при повторном просмотре коллекции?

2. Каждая точка возрождения ссылается на определенный пул объектов в зависимости от типа. RespawnManager проверяет, «занята» ли какая-либо из точек возрождения, а затем первая точка возрождения, которая свободна, будет использована для создания объекта, связанного с этой точкой возрождения.

3. вам следует взглянуть на ковариантные и контравариантные

4. «будет использоваться для создания объекта, связанного с этой точкой возрождения», как это достигается? вызывая метод Get?

5. Я отредактировал свой OP. Изначально я не хотел заполнять сообщение всем своим кодом.

Ответ №1:

Вы должны иметь возможность объявлять свое spawnpoints свойство в RespawnManager как List<SpawnPoint<Component>> вместо List<SpawnPoint<T>> . Это позволит вам полностью избавиться от <T> параметра type из RespawnManager и сделать его не общим.