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

时间:2018-11-08 22:17:40

标签: c# unity3d generics pooling

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

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

我的FXDistribrutor.cs类是附加到初始场景中的对象的组件,旨在永久存在于游戏的所有场景中。它具有对自身的静态引用,因此我可以从任何地方轻松调用它。最后更多关于这一点。我什至不确定这是否是“正确”的方法。但是效果很好。

FXDistributor为其可分配的每种类型的FX单元都有一个公共插槽,并为该类型的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单元进行这种处理?

2 个答案:

答案 0 :(得分:5)

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


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

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

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

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

BoxCollider

2 。将组件列表存储在字典中,并以List<Component> components; 作为键,将Type作为值。这样可以更轻松快捷地按List<Component>分组和查找组件。

Type

3 。剩下的真的很容易。使从“字典”中添加或从“字典”中检索池项的函数成为通用类型,然后使用Dictionary<Type, List<Component>> poolTypeDict; 在通用类型到Component类型之间或从通用类型转换为要求的任何类型之间进行转换返回。

4 。当您需要向字典中添加项目时,请检查Convert.ChangeType是否存在,如果存在,请检索现有的密钥 create add new {{ 3}},并使用Type函数将其保存到字典中。

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

将物品添加到池中后,使用Type

停用 GameObject

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

从池中检索物品后,请使用false

激活游戏对象

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

6 。要在使用完该项目后将其回收到池中,请不要调用component.gameObject.SetActive(true)函数。只需使用Destroy * 停用 GameObject。这样,下次您在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); } } LandingFX):

BumperFX

public class LandingFX : MonoBehaviour { ... }

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

public class BumperFX : MonoBehaviour { ... }

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

public LandingFX landingFxPrefab;
public BumperFX bumperFxPrefab;

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

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

当您需要从池中使用//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);

答案 1 :(得分:3)

我对解决方案并不满意,但是将一个不错的object pool与一个简单的Dictionary<K, V>结合使用会产生以下结果:

// pool of single object type, uses new for instantiation
public class ObjectPool<T> where T : new()
{
    // this will hold all the instances, notice that it's up to caller to make sure
    // the pool size is big enough not to reuse an object that's still in use
    private readonly T[] _pool = new T[_maxObjects];
    private int _current = 0;

    public ObjectPool()
    {
        // performs initialization, one may consider doing lazy initialization afterwards
        for (int i = 0; i < _maxObjects; ++i)
            _pool[i] = new T();
    }

    private const int _maxObjects = 100;  // Set this to whatever

    public T Get()
    {
        return _pool[_current++ % _maxObjects];
    }
}

// pool of generic pools
public class PoolPool
{
    // this holds a reference to pools of known (previously used) object pools
    // I'm dissatisfied with an use of object here, but that's a way around the generics :/
    private readonly Dictionary<Type, object> _pool = new Dictionary<Type, object>();

    public T Get<T>() where T : new()
    {
        // is the pool already instantiated?
        if (_pool.TryGetValue(typeof(T), out var o))
        {
            // if yes, reuse it (we know o should be of type ObjectPool<T>,
            // where T matches the current generic argument
            return ((ObjectPool<T>)o).Get();
        }

        // First time we see T, create new pool and store it in lookup dictionary
        // for later use
        ObjectPool<T> pool = new ObjectPool<T>();
        _pool.Add(typeof(T), pool);

        return pool.Get();
    }
}

现在,您只需执行以下操作即可:

pool.Get<A>().SayHello();
pool.Get<B>().Bark();

然而,这仍然留有改进的空间,因为它使用new而不是您的工厂方法实例化类,并且没有提供以通用方式自定义池大小的方法。