unity3d 如何将“enum”用于对象池

chhqkbe1  于 2023-02-13  发布在  其他
关注(0)|答案(2)|浏览(209)

我创建了一个对象池函数,当我在播放器脚本中调用Get()函数时,我不想给它传递一个像Get(0)这样的数字,而是像Get((int)ObjPrefabs.PlayerBullet)这样调用它。
所以我尝试使用"enum",但是我不知道如何将预置对象赋值给enum。

public class ObjectManagerFirst : MonoBehaviour
{
    public GameObject[] prefabs;

    public enum ObjPrefabs {PlayerBullet};
    List<GameObject>[] pools;

    void Awake()
    {
        pools = new List<GameObject>[prefabs.Length];

        for (int index = 0; index < pools.Length; index++)
        {
            pools[index] = new List<GameObject>();
        }
    }

    public GameObject Get(int index)
    {
        GameObject select = null;
            
        foreach(GameObject item in pools[index])
        {

            if(!item.activeSelf)
            {       
                select = item;
                select.SetActive(true);
                break;
            }
        }

        if(!select)
        {
            select = Instantiate(prefabs[index], transform);
            pools[index].Add(select);
        }

        return select;
    }
}

这是目前我在播放器脚本中调用它的方式:
GameObject bullet = objectManager.Get(0);
但我想:
GameObject bullet = objectManager.Get((int)ObjPrefabs.PlayerBullet);
我已经尝试了以下,但它没有工作:

List<int> ObjList = new List<int>();
int value = ObjList[(int)ObjPrefabs.PlayerBullet]
aemubtdh

aemubtdh1#

您可以将int值赋给enum,然后将其传递给function,下面是一个示例

enum ObjPrefabs
{
    PlayerBullet = 0,
    EnemyBullet = 1
}

然后像这样调用Get函数

GameObject bullet = objectManager.Get((int)ObjPrefabs.PlayerBullet);

一定会成功的。

k5ifujac

k5ifujac2#

与其使用int作为索引,为什么不坚持使用enum并使用

private Dictionary<ObjPrefabs, XXX> pools;

而是使用

public GameObject Get(ObjPrefabs type)
{
    if(!pools.TryGetValue(type, out var pool)
    {
        Debug.LogError($"Pool for {type} has not been initialized!");
        return null;
    }

    // otherwise get object from "pool"
}

有关XXX,请参见下文
现在,对于池本身,阵列是可以的,但更好的是,例如,简单地使用Queue
我建议使用一种更复杂但更容易处理的池化方法:

[Serializable]
public class Pool
{
    public class PoolObject : MonoBehaviour
    {
        private Pool _pool;

        public void Initialize (Pool pool)
        {
            _pool = pool;
        }

        // Use this component and method to release instances instead of calling Destroy!
        public void Release()
        {
            _pool.Release(this);
        }

        private void OnDestroy ()
        {
            Debug.LogWarning("Destroyed a pooled object! Rather use Release!", this);
            _pool.OnDestroyed(this);
        }
    }

    // In theory you could even get rid of this enum and use the prefab itself as key ;)
    [SerializeField] private ObjPrefab _type;
    [SerializeField] private GameObject _prefab;

    [SerializeField] [Min(1)] private int _initialAmount;

    private readonly Queue<PoolObject> _availableInstances = new();
    private readonly List<PoolObject> _hotInstaces = new();

    public ObjPrefabs Type => _type;

    public void Initialize (Transform inactiveParent)
    {
        for(var i = 0; i < _initialAmount; i++)
        {
            var instance = CreateInstance();
            instance.gameObject.SetActive(false);

            _availableInstances.Enqueue(instance);
        }
    }

    private PoolObject CreateInstance ()
    {
        var instance = Instantiate (_prefab, inactiveParent);
        if(! instance.TryGetComponent<PoolObject>(out var obj))
        {
            obj = instance.AddComponent<PoolObject>();
        }

        obj.Initialize(this);
    }

    private void OnDestroyed (PoolObject obj)
    {
        hotInstaces.Remove(obj);
    }

    public PoolObject GetInstance()
    {
        var instance = _avaialableInstances.Count != 0 ? _availableInstances.Dequeue() : CreateInstance();
        instance.gameObject.SetActive(true);
        hotInstances.Add(instance);
        return instance;
    }

    public void Release(PoolObject instance)
    {
        if(_hotInstances.Remove(instance))
        {
            instance.gameObject.SetActive(false);
            _availableInstances.Enqueue(instance);
        }
        else
        {
            Debug.LogError($"{instance} isn't an active object!");
        }
    }
}

最后你会用

[SerializeField] private Pool[] _pools;

private readonly Dictionary<ObjPrefabs, Pool> _activePools = new();

private void Awake ()
{
    foreach(var pool in _pools)
    {
        if(_activePools.ContainsKey(pool.Type))
        {
            Debug.LogError($"Duplicate pools for type {Type}!");
            continue;
        }

        pool.Initialize(transform);
        _activePools[pool.Type] = pool;
    }
}

public Pool.PoolObject Get(ObjPrefabs type)
{
    if(!pools.TryGetValue(type, out var pool)
    {
        Debug.LogError($"Pool for {type} has not been initialized!");
        return null;
    }

    return pool.GetInstance();
}

相关问题