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

标签 c# unity3d generics pooling

我对泛型的认识是它们可以帮助我简化我的池,但不知道如何。

我的池系统是简约的,但凌乱。现在变得笨拙和凌乱,而且凌乱。它不能很好地扩展...

我的 FXDistributor.cs 类是附加到初始场景中的对象的组件,旨在永久存在于游戏的所有场景中。它有一个对自身的静态引用,因此我可以轻松地从任何地方调用它。最后详细介绍了这个设计。我什至不确定这是否是做到这一点的“正确”方式。但它工作得很好。

FXDistributor 为它能够分发的每种类型的 FX 单元都有一个公共(public)插槽,以及一个用于此类 FX 池的数组,以及数组的索引和池的大小。

这里有两个例子:

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

    public LandingFX LndngFX;
    LandingFX[] _poolOfLndngFX;
    int _indexLndngFX, _poolSizeLndngFX = 5;

在 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 );
    }
}

在类的主体中,我为每种 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;
}

因此,当我想要这些 FX 之一并使用它时,我会从任何地方这样调用静态引用:
    FXDistributor.sRef.GimmeLandingFX( ).Bounce(
            bounce.point,
            bounce.tangentImpulse,
            bounce.normalImpulse 
            );

我如何使用泛型简化这种方法,以便我可以轻松且不那么困惑地为几十种类型的 FX 单位做这种事情?

最佳答案

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

仔细阅读您的问题和评论,您想避免GetComponent部分并仅代表组件 不是 游戏对象,以便您也可以直接访问组件。

如果这是您想要的,那么这就是 Unity 的 Component 是必须的。有关执行此操作所需的步骤,请参见下文。

请注意,当我说组件/脚本时,我指的是源自 MonoBehaviour 的脚本。可以附加到游戏对象或内置 Unity 组件,例如 RigidbodyBoxCollider .

1 .将组件/脚本存储到 Component 的列表中.

List<Component> components;

2 .使用 Type 将组件列表存储在字典中作为关键和List<Component>作为值(value)。这使得通过 Type 对组件进行分组和查找变得更加容易和快捷。 .
Dictionary<Type, List<Component>> poolTypeDict;

3 .剩下的真的很容易。使从字典中添加或检索池项目的函数成为通用函数,然后使用 Convert.ChangeType在泛型类型和 Component 之间进行转换类型或从泛型到要求返回的任何类型。

4 .当您需要向字典中添加项目时,请检查 Type存在,如果存在,检索现有 key ,创建并添加新的 Component Instantiate函数然后将其保存到字典中。

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

将项目添加到池中后 停用 带有 component.gameObject.SetActive(false) 的游戏对象

5 .当您需要从池中检索项目时,请检查 Type作为键存在然后检索值是 ListComponent .循环遍历组件并返回具有停用游戏对象的任何组件。您可以通过检查是否 component.gameObject.activeInHierarchy 来检查。是 false .

从池中检索项目后 激活 带有 component.gameObject.SetActive(true) 的游戏对象

如果未找到组件,您可以决定返回 null 或实例化新组件。

6 .要在使用完毕后将元素回收回池中,请不要调用 Destroy。功能。简单 停用 带有 component.gameObject.SetActive(false) 的游戏对象*.这将使您下次在 Dictionary 中搜索可用组件时能够找到该组件。和 List .

以下是脚本和组件的最小通用池系统示例:
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);
    }
}

用法:

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

示例预制脚本( LandingFXBumperFX ):
public class LandingFX : MonoBehaviour { ... }


public class BumperFX : MonoBehaviour { ... }

保存 Prefabs 引用的两个变量。您可以使用公共(public)变量并从编辑器分配它们,也可以使用 Resources API 加载它们。 .
public LandingFX landingFxPrefab;
public BumperFX bumperFxPrefab;

创建新的组件池并禁用自动调整大小
ComponentPool cmpPool = new ComponentPool();
cmpPool.autoExpand = false;

创建 2 LandingFX 和 BumperFX 组件的池。可以花任何 成分
//AddPrefab 2 objects type of LandingFX
cmpPool.AddPrefab(landingFxPrefab, 2);
//AddPrefab 2 objects type of BumperFX
cmpPool.AddPrefab(bumperFxPrefab, 2);

当您需要 LandingFX从池中,您可以按如下方式检索它们:
LandingFX lndngFX1 = cmpPool.GetAvailableObject(landingFxPrefab);
LandingFX lndngFX2 = cmpPool.GetAvailableObject(landingFxPrefab);

当您需要 BumperFX从池中,您可以按如下方式检索它们:
BumperFX bmpFX1 = cmpPool.GetAvailableObject(bumperFxPrefab);
BumperFX bmpFX2 = cmpPool.GetAvailableObject(bumperFxPrefab);

使用完检索到的组件后,将它们回收回池而不是销毁它们:
lndngFX1.RecyclePool();
lndngFX2.RecyclePool();
bmpFX1.RecyclePool();
bmpFX2.RecyclePool();

关于c# - 如何为组件/脚本创建泛型池系统?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53217000/

相关文章:

android - AndroidManifest 中的多个 ComponentDiscoveryService --> 合并冲突

xml - 是否有支持泛型的 Delphi 对象序列化库?

ios - iOS 构建中未显示 Unity 调试日志

c# - 访问 Json 序列化器选项以避免在 Azure Functions v3 中序列化 null

c# - 将事件或命令分配给 ResourceDictionary 中的 DataTemplate

c# - 查询 c# 列表以限制子项但返回父项

unity3d - 如何将文件写入 Oculus Quest 内部存储

C# 扩展方法编译/兼容性检查失败基于命名空间的顺序

json - 使用Argonaut创建通用JSON转换器

c# - MVP,类应该在哪里创建?