如何为组件/脚本创建泛型池系统?

Con*_*sed 8 c# generics pooling unity-game-engine

我对泛型的认识是,他们可以帮助我简化我的汇集,但无法弄清楚如何.

我的汇集系统简约,但很混乱.而现在变得笨拙和凌乱,还有MESSY.它不能很好地扩展......

我的FXDistribrutor.cs类是附加到初始场景中的对象的组件,旨在永久存在于游戏的所有场景中.它有自己的静态引用,所以我可以轻松地从任何地方调用它.更多关于最后的设计.我甚至不确定这是否是"正确"的方式.但它很好用.

FXDistributor为它能够分发的每种类型的FX单元都有一个公共插槽,为这种类型的FX池提供一个数组,以及一个数组的索引和池的大小.

这是两个例子:

    public BumperFX BmprFX;
    BumperFX[] _poolOfBumperFX;
    int _indexBumperFX, _poolSize = 10;

    public LandingFX LndngFX;
    LandingFX[] _poolOfLndngFX;
    int _indexLndngFX, _poolSizeLndngFX = 5;
Run Code Online (Sandbox Code Playgroud)

在Unity Start调用中,我填写每个FX单元的池:

void Start(){

    _poolOfBumperFX = new BumperFX[_poolSize];
    for (var i = 0; i < _poolSize; i++) {
    _poolOfBumperFX[i] = Instantiate(BmprFX, transform );
    }

    _poolOfLndngFX = new LandingFX[_poolSizeLndngFX];
    for ( var i = 0; i < _poolSizeLndngFX; i++ ) {
    _poolOfLndngFX[i] = Instantiate( LndngFX, transform );
    }
}
Run Code Online (Sandbox Code Playgroud)

在课程的主体中,我为每种FX类型提供了一系列方法,以便在需要的地方提供它们:

public LandingFX GimmeLandingFX ( ){
    if ( _indexLndngFX == _poolSizeLndngFX ) _indexLndngFX = 0;
    var lndngFX = _poolOfLndngFX[_indexLndngFX];
    _indexLndngFX++; return lndngFX;
}
public BumperFX GimmeBumperFX ( ) {
    if ( _indexBumperFX == _poolSize ) _indexBumperFX = 0;
    var bumperFX = _poolOfBumperFX[_indexBumperFX];
    _indexBumperFX++;   return bumperFX;
}
Run Code Online (Sandbox Code Playgroud)

因此,当我想要其中一个FX并使用它时,我会像这样从任何地方调用静态引用:

    FXDistributor.sRef.GimmeLandingFX( ).Bounce(
            bounce.point,
            bounce.tangentImpulse,
            bounce.normalImpulse 
            );
Run Code Online (Sandbox Code Playgroud)

我如何使用泛型简化这种方法,这样我可以轻松而不那么混乱地为几十种类型的FX单元做这类事情?

Pro*_*mer 5

在Unity中,Instantiate()Destroy()函数用于创建对象的副本,尤其是预制件并销毁它们.在池化时,池对象通常在池中表示为GameObject类型.当您需要从池中访问组件时,首先检索池GameObject,然后使用该GetComponent函数从GameObject中检索组件.


仔细阅读您的问题和评论,您希望避免使用该GetComponent部分并仅表示组件而不是 GameObject,以便您也可以直接访问组件.

如果这是你想要的,那么这Component就是需要Unity的地方.请参阅下文,了解执行此操作所需的步骤

请注意,当我说组件/脚本时,我指的是从中派生的脚本MonoBehaviour可以附加到GameObjects或内置的Unity组件,如RigidbodyBoxCollider.

1.将组件/脚本存储到List of Component.

List<Component> components;
Run Code Online (Sandbox Code Playgroud)

2.将"组件列表"存储在"词典"中,Type并将其作为键和List<Component>值.这样可以更轻松,更快速地分组和查找组件Type.

Dictionary<Type, List<Component>> poolTypeDict;
Run Code Online (Sandbox Code Playgroud)

3.其余的很简单.使从池中添加或检索池项的函数是通用的,然后用于Convert.ChangeType在类型的泛型类型之间Component或从泛型到请求返回的类型之间进行转换.

4.当您需要将项添加到Dictionary时,检查是否Type存在,如果存在,则检索现有密钥,使用该函数创建添加新密钥,然后将其保存到Dictionary.ComponentInstantiate

如果Type尚不存在,则无需从中检索任何数据Dictionary.只需创建一个新的并将其添加到Dictionary中Type.

将项目添加到池后,取消激活 GameObjectcomponent.gameObject.SetActive(false)

5.当您需要检索从池中的项目,检查是否Type存在关键然后检索这是值ListComponent.遍历组件并返回具有取消激活的GameObject的任何组件.您可以通过检查,如果检查component.gameObject.activeInHierarchyfalse.

从池中检索项目后,激活 GameObjectcomponent.gameObject.SetActive(true)

如果未找到任何组件,则可以决定返回null或实例化新组件.

6.要在完成使用后将项目回收到池中,请不要调用该Destroy功能.只需用*取消激活 GameObject即可component.gameObject.SetActive(false).这将使得能够在组件中发现下一次您搜索可用的组件DictionaryList.

以下是脚本和组件的最小通用池系统示例:

public class ComponentPool
{
    //Determines if pool should expand when no pool is available or just return null
    public bool autoExpand = true;
    //Links the type of the componet with the component
    Dictionary<Type, List<Component>> poolTypeDict = new Dictionary<Type, List<Component>>();

    public ComponentPool() { }


    //Adds Prefab component to the ComponentPool
    public void AddPrefab<T>(T prefabReference, int count = 1)
    {
        _AddComponentType<T>(prefabReference, count);
    }

    private Component _AddComponentType<T>(T prefabReference, int count = 1)
    {
        Type compType = typeof(T);

        if (count <= 0)
        {
            Debug.LogError("Count cannot be <= 0");
            return null;
        }

        //Check if the component type already exist in the Dictionary
        List<Component> comp;
        if (poolTypeDict.TryGetValue(compType, out comp))
        {
            if (comp == null)
                comp = new List<Component>();

            //Create the type of component x times
            for (int i = 0; i < count; i++)
            {
                //Instantiate new component and UPDATE the List of components
                Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
                Component instance = Instantiate(original);
                //De-activate each one until when needed
                instance.gameObject.SetActive(false);
                comp.Add(instance);
            }
        }
        else
        {
            //Create the type of component x times
            comp = new List<Component>();
            for (int i = 0; i < count; i++)
            {
                //Instantiate new component and UPDATE the List of components
                Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
                Component instance = Instantiate(original);
                //De-activate each one until when needed
                instance.gameObject.SetActive(false);
                comp.Add(instance);
            }
        }

        //UPDATE the Dictionary with the new List of components
        poolTypeDict[compType] = comp;

        /*Return last data added to the List
         Needed in the GetAvailableObject function when there is no Component
         avaiable to return. New one is then created and returned
         */
        return comp[comp.Count - 1];
    }


    //Get available component in the ComponentPool
    public T GetAvailableObject<T>(T prefabReference)
    {
        Type compType = typeof(T);

        //Get all component with the requested type from  the Dictionary
        List<Component> comp;
        if (poolTypeDict.TryGetValue(compType, out comp))
        {
            //Get de-activated GameObject in the loop
            for (int i = 0; i < comp.Count; i++)
            {
                if (!comp[i].gameObject.activeInHierarchy)
                {
                    //Activate the GameObject then return it
                    comp[i].gameObject.SetActive(true);
                    return (T)Convert.ChangeType(comp[i], typeof(T));
                }
            }
        }

        //No available object in the pool. Expand array if enabled or return null
        if (autoExpand)
        {
            //Create new component, activate the GameObject and return it
            Component instance = _AddComponentType<T>(prefabReference, 1);
            instance.gameObject.SetActive(true);
            return (T)Convert.ChangeType(instance, typeof(T));
        }
        return default(T);
    }
}

public static class ExtensionMethod
{
    public static void RecyclePool(this Component component)
    {
        //Reset position and then de-activate the GameObject of the component
        GameObject obj = component.gameObject;
        obj.transform.position = Vector3.zero;
        obj.transform.rotation = Quaternion.identity;
        component.gameObject.SetActive(false);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

它可以采用任何预制组件脚本.预制件用于此,因为合并的对象通常是预制件实例化并等待使用.

示例预制脚本(LandingFX,BumperFX):

public class LandingFX : MonoBehaviour { ... }
Run Code Online (Sandbox Code Playgroud)

public class BumperFX : MonoBehaviour { ... }
Run Code Online (Sandbox Code Playgroud)

两个变量用于保存Prefabs参考.您可以使用公共变量并从编辑器中分配它们,也可以使用Resources API加载它们.

public LandingFX landingFxPrefab;
public BumperFX bumperFxPrefab;
Run Code Online (Sandbox Code Playgroud)

创建新的组件池并禁用自动调整大小

ComponentPool cmpPool = new ComponentPool();
cmpPool.autoExpand = false;
Run Code Online (Sandbox Code Playgroud)

为LandingFX和BumperFX组件创建2个池.它可以采取任何组件

//AddPrefab 2 objects type of LandingFX
cmpPool.AddPrefab(landingFxPrefab, 2);
//AddPrefab 2 objects type of BumperFX
cmpPool.AddPrefab(bumperFxPrefab, 2);
Run Code Online (Sandbox Code Playgroud)

当您需要LandingFX池中的a时,可以按如下方式检索它们:

LandingFX lndngFX1 = cmpPool.GetAvailableObject(landingFxPrefab);
LandingFX lndngFX2 = cmpPool.GetAvailableObject(landingFxPrefab);
Run Code Online (Sandbox Code Playgroud)

当您需要BumperFX池中的a时,可以按如下方式检索它们:

BumperFX bmpFX1 = cmpPool.GetAvailableObject(bumperFxPrefab);
BumperFX bmpFX2 = cmpPool.GetAvailableObject(bumperFxPrefab);
Run Code Online (Sandbox Code Playgroud)

当您使用检索到的组件完成后,将它们回收到池中而不是销毁它们:

lndngFX1.RecyclePool();
lndngFX2.RecyclePool();
bmpFX1.RecyclePool();
bmpFX2.RecyclePool();
Run Code Online (Sandbox Code Playgroud)