В Unity функции Instantiate()
и Destroy()
используются для создания копий объектов, особенно префабов, и их уничтожения. Когда дело доходит до пула, объект пула обычно представляется в пуле как тип GameObject. Когда вам нужно получить доступ к компоненту из пула, вы сначала получаете пул GameObject, а затем используете функцию GetComponent
для извлечения компонента из GameObject.
Внимательно читая свой вопрос и комментарии, вы хотите избежать раздела GetComponent
и представлять только компоненты , а не GameObject, чтобы вы также могли получить прямой доступ к компонентам.
Если это то, что вам нужно, то именно здесь требуется Unity Component
. Ниже приведены шаги, необходимые для этого.
Обратите внимание, что когда я говорю компонент / скрипт, я имею в виду ваши скрипты, которые происходят из MonoBehaviour
, которые могут быть присоединены к GameObjects или встроенным компонентам Unity, таким как Rigidbody
и BoxCollider
.
1 . Сохраните компоненты / сценарии в списке Component
.
List<Component> components;
2 . Сохраните список компонентов в словаре, указав Type
в качестве ключа и List<Component>
в качестве значения. Это упрощает и ускоряет группирование и поиск компонентов по Type
.
Dictionary<Type, List<Component>> poolTypeDict;
3 . Остальное действительно легко. Сделайте функцию, которая добавляет или получает элементы пула из словаря и в него, чтобы они были универсальными, затем используйте Convert.ChangeType
для приведения между универсальным типом к типу Component
или из универсального к тому типу, который запрашивается подлежит возврату.
4 . Когда вам нужно добавить элемент в словарь, проверьте, существует ли еще Type
, если это так, извлеките существующий ключ, создайте и добавьте new Component
к нему с помощью функции Instantiate
, затем сохраните его в Словаре.
Если Type
еще не существует, нет необходимости извлекать данные из Dictionary
. Просто создайте новый и добавьте его в словарь с Type
.
После добавления предмета в пул деактивация GameObject с component.gameObject.SetActive(false)
5 . Когда вам нужно извлечь элемент из пула, проверьте, существует ли Type
в качестве ключа, а затем извлеките значение, равное List
из Component
. Цикл по компонентам и вернуть любой компонент, который имеет деактивированный GameObject. Вы можете проверить это, проверив, является ли component.gameObject.activeInHierarchy
false
.
Как только вы получите предмет из пула активируйте GameObject с component.gameObject.SetActive(true)
Если компонент не найден, вы можете решить вернуть либо ноль, либо создать новый компонент.
6 . Чтобы вернуть элемент обратно в пул после его использования, вы не должны вызывать функцию 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 { ... }
и
public class BumperFX : MonoBehaviour { ... }
Две переменные для хранения ссылок Prefabs. Вы можете использовать открытые переменные и назначить их из редактора или загрузить их с помощью Resources API .
public LandingFX landingFxPrefab;
public BumperFX bumperFxPrefab;
Создание нового пула компонентов и отключение автоматического изменения размера
ComponentPool cmpPool = new ComponentPool();
cmpPool.autoExpand = false;
Создание 2 пулов для компонентов LandingFX и BumperFX. Это может занять любой компонент
//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();