在Unity中,
Instantiate()
和
Destroy()
函数用于创建对象的副本,特别是预制件,并销毁它们。当涉及到对象池时,池对象通常表示为GameObject类型。当需要从池中访问组件时,首先检索池GameObject,然后使用
GetComponent
函数从GameObject检索组件。
阅读您的问题和评论,您想避免
GetComponent
部分,仅表示组件而不是GameObject,以便您也可以直接访问组件。
如果这是您想要的,那么Unity的
Component
就是必需的。请参阅下面所需的步骤。
请注意,当我说组件/脚本时,我指的是从
MonoBehaviour
派生并可附加到GameObject或内置Unity组件(如
Rigidbody
和
BoxCollider
)的脚本。
1. 将组件/脚本存储到
Component
列表中。
List<Component> components;
2. 使用Type
作为键,List<Component>
作为值,将组件列表存储在字典中。这样可以更轻松、更快速地按Type
分组和查找组件。
Dictionary<Type, List<Component>> poolTypeDict;
3. 其余部分非常简单。将添加或检索池项的函数设置为通用函数,然后使用Convert.ChangeType
在通用类型和Component
类型之间进行转换,或从通用类型转换为请求返回的任何其他类型。
4. 当需要向字典中添加项目时,请检查Type
是否已存在,如果存在,则检索现有的键,使用Instantiate
函数创建并添加新的Component
,然后将其保存到字典中。
如果Type
尚不存在,则无需从Dictionary
中检索任何数据。只需创建新的并将其与其Type
一起添加到字典中即可。
一旦将项目添加到池中,请停用带有component.gameObject.SetActive(false)
的GameObject。
5. 当你需要从池中检索一个项目时,检查Type
是否存在作为键,然后检索值,即Component
的List
。循环遍历组件并返回任何具有非激活的游戏对象的组件。可以通过检查component.gameObject.activeInHierarchy
是否为false
来检查这一点。
一旦从池中检索到项目,使用component.gameObject.SetActive(true)
激活游戏对象
如果未找到组件,则可以决定返回null或实例化新组件。
6. 当你完成使用一个项目并将其回收到池中时,不要调用Destroy
函数。只需使用component.gameObject.SetActive(false)
使游戏对象处于非激活状态。这将使组件能够在下次搜索Dictionary
和List
中的可用组件时被找到。
以下是脚本和组件的最小通用池系统示例:
public class ComponentPool
{
public bool autoExpand = true;
Dictionary<Type, List<Component>> poolTypeDict = new Dictionary<Type, List<Component>>();
public 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;
}
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
if (comp == null)
comp = new List<Component>();
for (int i = 0; i < count; i++)
{
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
else
{
comp = new List<Component>();
for (int i = 0; i < count; i++)
{
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
poolTypeDict[compType] = comp;
return comp[comp.Count - 1];
}
public T GetAvailableObject<T>(T prefabReference)
{
Type compType = typeof(T);
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
for (int i = 0; i < comp.Count; i++)
{
if (!comp[i].gameObject.activeInHierarchy)
{
comp[i].gameObject.SetActive(true);
return (T)Convert.ChangeType(comp[i], typeof(T));
}
}
}
if (autoExpand)
{
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)
{
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 { ... }
有两个变量用于保存预制件的引用。您可以使用公共变量并从编辑器中分配它们,或使用资源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();