在 Unity 中,Instantiate() 和 Destroy() 函数用于创建对象的副本,尤其是预制件并销毁它们。说到池化,池对象通常在池中表示为 GameObject 的类型。当您需要从池中访问组件时,您首先检索池游戏对象,然后使用GetComponent 函数从游戏对象中检索组件。
仔细阅读您的问题和 cmets,您希望避免使用 GetComponent 部分并仅表示组件而不是游戏对象,以便您也可以直接访问组件。
如果这是您想要的,那么这就是需要 Unity 的 Component 的地方。请参阅下文了解执行此操作所需的步骤。
请注意,当我说组件/脚本时,我指的是从 MonoBehaviour 派生的脚本,这些脚本可以附加到游戏对象或内置 Unity 组件,例如 Rigidbody 和 BoxCollider。
1。将组件/脚本存储到Component 的列表中。
List<Component> components;
2。将组件列表存储在字典中,以 Type 作为键,List<Component> 作为值。这使得通过Type 对组件进行分组和查找变得更加容易和快捷。
Dictionary<Type, List<Component>> poolTypeDict;
3。其余的真的很容易。使从 Dictionary 中添加或检索池项的函数为泛型,然后使用 Convert.ChangeType 在泛型类型之间转换为 Component 类型或从泛型转换为请求返回的任何类型。
4。当您需要向 Dictionary 添加项目时,检查 Type 是否存在,如果存在,则检索现有密钥,create 和 add new Component 到使用Instantiate 函数将其保存到字典中。
如果Type 尚不存在,则无需从Dictionary 检索任何数据。只需创建一个新的并使用其Type 将其添加到字典中。
将项目添加到池后停用游戏对象component.gameObject.SetActive(false)
5。当您需要从池中检索项目时,请检查 Type 是否作为键存在,然后检索 List 的值 Component。循环组件并返回任何具有停用游戏对象的组件。您可以通过检查 component.gameObject.activeInHierarchy 是否为 false 来检查。
一旦您从池中检索项目激活游戏对象component.gameObject.SetActive(true)
如果没有找到组件,您可以决定返回 null 或实例化新组件。
6。要在使用完毕后将项目回收回池中,请不要调用 Destroy 函数。只需使用component.gameObject.SetActive(false)*取消激活 GameObject。这将使您下次在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 { ... }
和
public class BumperFX : MonoBehaviour { ... }
保存预制件引用的两个变量。您可以使用公共变量并从编辑器中分配它们,也可以使用Resources API 加载它们。
public LandingFX landingFxPrefab;
public BumperFX bumperFxPrefab;
创建新的组件池并禁用自动调整大小
ComponentPool cmpPool = new ComponentPool();
cmpPool.autoExpand = false;
为 LandingFX 和 BumperFX 组件创建 2 个池。它可以采用任何组件
//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();