我对泛型的了解是,它们可以帮助我简化共享池,但不知道如何实现。
我的共享系统是简约的,但是很混乱。现在变得笨拙和混乱,以及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单元进行这种处理?
答案 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
5 。当您需要从池中检索项目时,请检查component.gameObject.SetActive(false)
是否作为键存在,然后检索Type
中List
的值。遍历组件,并返回任何具有停用的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
而不是您的工厂方法实例化类,并且没有提供以通用方式自定义池大小的方法。