如何为组件/脚本创建泛型共享系统? [英] How to create Generics Pooling System for components/scripts?

查看:48
本文介绍了如何为组件/脚本创建泛型共享系统?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对泛型的了解是,它们可以帮助我简化共享池,但不知道如何实现.

My awareness of Generics is that they can help me streamline my pooling, but can't figure out how.

我的共享系统是简约的,但是很凌乱.现在变得笨拙和混乱,以及MESSY.伸缩性不好...

My pooling system is minimalistic, but messy. And now getting unwieldy and messy, and MESSY. It doesn't scale nicely...

我的FXDistribrbrutor.cs类是附加到初始场景中的对象的组件,旨在永久存在于游戏的所有场景中.它具有对自身的静态引用,因此我可以从任何地方轻松调用它.最后介绍更多有关该功能的信息.我什至不确定这是否是执行此操作的正确"方法.但是效果很好.

My FXDistribrutor.cs class is a component attached to an object in the initial scene, designed to permanently exist throughout all scenes of the game. It has a static reference to itself, so that I can call into it from anywhere easily. More on that contrivance at the end. I'm not even sure if that's the 'right' way to do this. But it works nicely.

FXDistributor为其可分配的每种类型的FX单元都有一个公共插槽,并为该类型的FX池提供一个数组,并为该数组和池的大小提供索引.

FXDistributor has a public slot for each type of FX Unit it is able to distribute, and an array for the pool of this type of FX, and an index for the array and a size of the pool.

这里有两个例子:

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

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

在Unity Start呼叫中,我填充了每个FX单元的池:

In the Unity Start call, I fill the pools of each FX Unit:

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类型提供了一堆方法,以将它们提供给需要的任何地方:

And in the body of the class I have a bunch of methods for each FX type, to provide them to wherever they're needed:

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并使用它时,我可以从任何地方这样调用静态引用:

So when I want one of these FX, and to use it, I call like this, from anywhere, into the static reference:

    FXDistributor.sRef.GimmeLandingFX( ).Bounce(
            bounce.point,
            bounce.tangentImpulse,
            bounce.normalImpulse 
            );

我如何使用泛型简化此方法,以便可以轻松,轻松地为数十种类型的FX单元进行这种处理?

推荐答案

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

In Unity, the Instantiate() and Destroy() functions are used to create copy of objects especially prefabs and destroy them. When it comes to pooling, the pool object is usually represented in the pool as a Type of GameObject. When you need to access a component from the pool you first retrieve the pool GameObject then use the GetComponent function to retrieve the component from a GameObject.

仔细阅读您的问题和评论,您要避免使用 GetComponent 部分,而仅表示组件 GameObject,以便您也可以直接访问这些组件.

Reading your question and comments carefully, you want to avoid the GetComponent section and represent just the components not the GameObject so that you can also access the components directly.

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

If this is what you want then this is where Unity's Component is required. See below for steps required to do this.

请注意,当我说组件/脚本时,我指的是源自 MonoBehaviour 的脚本,这些脚本可以附加到GameObjects或内置Unity组件(例如 Rigidbody) BoxCollider .

Note that when I say component/script, I am referring to your scripts that derive from MonoBehaviour that can be attached to GameObjects or built-in Unity components such as Rigidbody and BoxCollider.

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

1. Store the components/scripts to a List of Component.

List<Component> components;

2 .将组件列表存储在字典中,并以 Type 作为键,并以 List< Component> 作为值.这使按 Type 分组和查找组件变得更加容易和快捷.

2. Store the List of Components in a Dictionary with Type as the key and List<Component> as the value. This makes it easier and faster to group and find components by Type.

Dictionary<Type, List<Component>> poolTypeDict;

3 .剩下的真的很容易.使添加或从字典"中检索池项的函数成为通用的,然后使用 Convert.ChangeType 在通用类型与

3. The rest is really easy. Make the function that adds or retrieves the pool items from and to the Dictionary to be generic then use Convert.ChangeType to cast between the generic type to Component type or from generic to what ever type that is requested to be returned.

4 .当您需要向字典中添加项目时,请检查 Type 是否存在,如果存在,请检索现有的键, create add 新增了 Component 并带有 Instantiate 函数,然后将其保存到字典中.

4. When you need to add item to the Dictionary, check if the Type exist yet, if it does, retrieve the existing key, create and add new Component to it with the Instantiate function then save it to the Dictionary.

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

If the Type doesn't exist yet, no need to retrieve any data from the Dictionary. Simply create new one and add it to the Dictionary with its Type.

将物品添加到池中后,停用带有 component.gameObject.SetActive(false)

5 .当您需要从池中检索项目时,请检查 Type 是否作为键存在,然后检索 Component List 的值.遍历组件,并返回任何具有停用的GameObject的组件.您可以通过检查 component.gameObject.activeInHierarchy 是否为 false 来进行检查.

5. When you need to retrieve an item from the pool, check if the Type exist as key then retrieve the value which is List of Component. Loop over the components and return any component that has a de-activated GameObject. You can check that by checking if component.gameObject.activeInHierarchy is false.

一旦从池中检索到物品,就使用 component.gameObject.SetActive(true)

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

If no component is found, you can decide to either return null or instantiate new component.

6 .要在使用完毕后将其回收到池中,请不要调用 Destroy 函数.只需使用 component.gameObject.SetActive(false) * 停用 GameObject.这样,下次您在 Dictionary List 中搜索可用的组件时,就可以找到该组件.

6. To recycle the item back to the pool when you're done using it, you don't call the Destroy function. Simply de-activate the GameObject with component.gameObject.SetActive(false)*. This will make the component able to be found next time you search for available components in the Dictionary and 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);
    }
}

用法:

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

It can take a any prefab component script. Prefabs are used for this since pooled objects are usually prefabs instantiated and waiting to be used.

示例预制脚本( LandingFX BumperFX ):

public class LandingFX : MonoBehaviour { ... }

public class BumperFX : MonoBehaviour { ... }

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

Two variables to hold the Prefabs references. You can either use public variables and assign them from the Editor or load them with the Resources API.

public LandingFX landingFxPrefab;
public BumperFX bumperFxPrefab;

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

Create new Component Pool and disable auto-resize

ComponentPool cmpPool = new ComponentPool();
cmpPool.autoExpand = false;

为LandingFX和BumperFX组件创建 2 个池.它可能需要任何组件

Create 2 pools for LandingFX and BumperFX components. It can take any component

//AddPrefab 2 objects type of LandingFX
cmpPool.AddPrefab(landingFxPrefab, 2);
//AddPrefab 2 objects type of BumperFX
cmpPool.AddPrefab(bumperFxPrefab, 2);

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

When you need a LandingFX from the pool, you can retrieve them as below:

LandingFX lndngFX1 = cmpPool.GetAvailableObject(landingFxPrefab);
LandingFX lndngFX2 = cmpPool.GetAvailableObject(landingFxPrefab);

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

When you need a BumperFX from the pool, you can retrieve them as below:

BumperFX bmpFX1 = cmpPool.GetAvailableObject(bumperFxPrefab);
BumperFX bmpFX2 = cmpPool.GetAvailableObject(bumperFxPrefab);

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

When you're done using the retrieved component, recycle them back to the pool instead of destroying them:

lndngFX1.RecyclePool();
lndngFX2.RecyclePool();
bmpFX1.RecyclePool();
bmpFX2.RecyclePool();

这篇关于如何为组件/脚本创建泛型共享系统?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆