Простой пул объектов

Что такое пул объектов и для чего он нужен? Допустим, у нас в игре персонаж может стрелять некими снарядами, и вроде бы всё просто, нужно создавать эти снаряды, а потом уничтожать, когда они сталкиваются с другими объектами. Но, дело в том, что процессы создания/уничтожения, достаточно ресурсоемки, особенно если вызывать их постоянно. Поэтому, если требуется частое использование одинаковых объектов, для этой задачи и нужен пул. Смысл в том, что мы один раз создаем сразу несколько колонов и используем их по очередности. Таким образом, в процессе игры мы не будет создавать новые объекты каждый раз, вместо этого у нас будет массив клонов.

Итак, нам нужно следующее. Возможность создавать несколько пулов. Каждый пул должен иметь свой идентификатор (имя) и по этому ID мы запрашиваем объекты. Дополнительно, возможность уничтожить указанный пул. Плюс, для каждого пула нам нужна опция, автоматического изменения размера. То есть, если свободных объектов не окажется, то будут созданы новые.

За всё этого дело, отвечает класс Pool:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public static class Pool {

	private static List<Part> pools;

	struct Part 
	{
		public string name;
		public List<PoolComponent> prefab;
		public bool resize;
		public Transform parent;
	}

	public static void CreatePool(PoolComponent sample, string name, int count, bool autoResize)
	{
		if(pools == null || count <= 0 || name.Trim() == string.Empty || sample == null) return;

		Part p = new Part();
		p.prefab = new List<PoolComponent>();
		p.name = name;
		p.resize = autoResize;
		p.parent = new GameObject("Pool-" + name).transform;

		for(int i = 0; i < count; i++)
		{
			p.prefab.Add(AddObject(sample, name, i, p.parent));
		}

		pools.Add(p);
		Debug.Log(" Добавлен пул: " + name);
	}

	static PoolComponent AddObject(PoolComponent sample, string name, int index, Transform parent)
	{
		PoolComponent comp = GameObject.Instantiate(sample) as PoolComponent;
		comp.gameObject.name = name + "-" + index;
		comp.transform.parent = parent;
		comp.gameObject.SetActive(false);
		return comp;
	}

	static void AutoResize(Part part, int index)
	{
		part.prefab.Add(AddObject(part.prefab[0], part.name, index, part.parent));
	}

	public static PoolComponent GetObject(string name, Vector3 position, Quaternion rotation)
	{
		if(pools == null) return null;

		foreach(Part part in pools)
		{
			if(string.Compare(part.name, name) == 0)
			{
				foreach(PoolComponent comp in part.prefab)
				{
					if(!comp.isActiveAndEnabled)
					{
						comp.transform.rotation = rotation;
						comp.transform.position = position;
						comp.gameObject.SetActive(true);
						return comp;
					}
				}

				if(part.resize)
				{
					AutoResize(part, part.prefab.Count);
					return part.prefab[part.prefab.Count-1];
				}
			}
		}

		return null;
	}

	public static void DestroyPool(string name)
	{
		if(pools == null) return;

		int j = 0;

		foreach(Part p in pools)
		{
			if(string.Compare(p.name, name) == 0)
			{
				GameObject.Destroy(p.parent.gameObject);
				pools.RemoveAt(j);
				Debug.Log(" Уничтожен пул: " + name);
				return;
			}

			j++;
		}
	}

	public static void Initialize()
	{
		pools = new List<Part>();
	}
}

Здесь нужно обратить внимание на инициализацию пула. Дело в том, что так как мы используем статик класс, то при переходе на другую сцену, необходимо удалять ссылки на мертвые объекты. То есть, в каждой сцене нужно вызывать функцию Initialize.

Напишем небольшой скрипт:

using UnityEngine;
using System.Collections;

[AddComponentMenu("Pool/Pool Initializer")]

public class PoolInitializer : MonoBehaviour {

	void Awake()
	{
		Pool.Initialize();
	}
}

Остается только добавить его в каждую сцену игры, повесить например на камеру.

Сам пул работает с классом PoolComponent:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

[AddComponentMenu("Pool/Pool Component")]

public class PoolComponent : MonoBehaviour {

	[SerializeField] private Transform _transform;
	[SerializeField] private Rigidbody _rigidbody;

	public Transform m_Transform
	{
		get{ return _transform; }
	}

	public Rigidbody m_Rigidbody
	{
		get{ return _rigidbody; }
	}

}

Этот скрипт мы вешаем на объект, на основе которого создаем пул. В текущем виде, у нас есть два поля Transform и Rigidbody, при необходимости конечно можно добавить еще. Всё зависит от конкретных нужд.

Простой пул объектов

Логика простая. Так как пул работает только с классом, то мы сами решаем какие переменные будет содержать этот класс.

Использование:

Создать новый пул объектов:

void Start () 
{
	Pool.CreatePool(sample, name, count, false);
}

Вникание!
Так как инициализация происходит в Awake, то создавать пулы нужно позже, например в Start.

Вызов объекта, допустим, это пуля:

if(Input.GetMouseButtonDown(0))
{
	PoolComponent bullet = Pool.GetObject(poolName, spawn.point, Quaternion.identity);

	if(bullet) 
	{
		bullet.m_Rigidbody.velocity = direction * speed;
	}
}

Получается так. Мы запрашиваем компонент, отправляя при этом: имя пула, место где должна появится пуля и вращение. И если есть свободный клон, обращаемся к переменной Rigidbody. Всё достаточно просто.

Скачать скрипты:

Внимание! Посетители, находящиеся в группе Гости, не могут скачивать файлы.
Тестировалось на: Unity 5.4.0

Комментариев 5

Офлайн
Какие скрипты куда вешать.
Офлайн
Light 29 июля 2017
Эйнштейн, как использовать написано в статье. Что именно не понятно?
Офлайн
Подскажите пожалуйста, как использовать эти скрипты? У меня задача такая - спавнить животных, но убиваются они из другого скрипта. как настроить пул вообще? Я мало знаком с такой системой(
Офлайн
Light 10 августа 2016
Vitremer, вызов Pool.Initialize(); нужно делать каждый раз после загрузки сцены, поэтому в Awake через другой класс. Чтобы в Start можно было сразу создавать необходимые пулы.
Офлайн
Vitremer 10 августа 2016
Спасибо полезная статейка. Только по поводу того что Static вызывается в Awake()-не уверен-Static должен вызываться при компиляции кода, а Awake()-это уже событие MonoBehaviour-так что сомнения на этот счёт-буду у компа проверю...
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика