Ни для кого не секрет, что использованные объекты надо "чистить" и удалять со сцены. Однако в действительности это достаточно вредный процесс. Особенно когда мы говорим об объектах которые очень часто создаются/удаляются.
Это могут быть и мобы, и пули, или любые другие мелкие и возобновляемые объекты. Суть в том, чтобы не уничтожать а изначально вгрузить на уровень предполагаемое кол-во объектов и активировать их понадобности, возвращая потом обратно в пул.
Вот скрипт.
Обзывать обязательно GameObjectPool.cs
Code
using UnityEngine;
using System;
using System.Collections;
namespace pools
{
public class GameObjectPool {
private GameObject _prefab;
private Stack available;
private ArrayList all;
private Action<GameObject> initAction;
private bool setActiveRecursively;
// ---- getters & setters ----
#region getters & setters
// returns the prefab being used by the pool.
public GameObject prefab {
get { return _prefab; }
}
// returns the number of active objects.
public int numActive {
get { return all.Count - available.Count; }
}
// returns the number of available objects.
public int numAvailable {
get { return available.Count; }
}
#endregion
// ---- constructor ----
#region constructor
public GameObjectPool(GameObject prefab, uint initialCapacity, Action<GameObject> initAction, bool setActiveRecursively) {
this._prefab = prefab;
this.initAction = initAction;
this.setActiveRecursively = setActiveRecursively;
available = (initialCapacity > 0) ? new Stack((int) initialCapacity) : new Stack();
all = (initialCapacity > 0) ? new ArrayList((int) initialCapacity) : new ArrayList();
}
#endregion
// ---- public methods ----
#region public methods
public GameObject Spawn(Vector3 position, Quaternion rotation) {
GameObject result;
if (available.Count == 0){
// create an object and initialize it.
result = GameObject.Instantiate(prefab, position, rotation) as GameObject;
// run optional initialization method on the object
if (initAction != null) initAction(result);
all.Add(result);
} else {
result = available.Pop() as GameObject;
// get the result's transform and reuse for efficiency.
// calling gameObject.transform is expensive.
Transform resultTrans = result.transform;
resultTrans.position = position;
resultTrans.rotation = rotation;
SetActive(result, true);
}
return result;
}
public bool Destroy(GameObject target) {
if (!available.Contains(target)) {
available.Push(target);
SetActive(target, false);
return true;
}
return false;
}
// Unspawns all the game objects created by the pool.
public void DestroyAll() {
for (int i=0; i<all.Count; i++) {
GameObject target = all[i] as GameObject;
if (target.active) Destroy(target);
}
}
// Unspawns all the game objects and clears the pool.
public void Clear() {
DestroyAll();
available.Clear();
all.Clear();
}
public bool Unspawn (GameObject obj)
{
if (!available.Contains (obj)) {
// Make sure we don't insert it twice.
available.Push (obj);
SetActive (obj, false);
return true;
// Object inserted back in stack.
}
return false;
// Object already in stack.
}
// Applies the provided function to some or all of the pool's game objects.
public void ForEach(Action<GameObject> action, bool activeOnly) {
for (int i=0; i<all.Count; i++){
GameObject target = all[i] as GameObject;
if (!activeOnly || target.active) action(target);
}
}
public void prePopulate (int num)
{
GameObject[] array = new GameObject[num];
for (int i = 0; i < num; i++) {
array[i] = Spawn (Vector3.zero, Quaternion.identity);
SetActive (array[i], true);
}
for (int j = 0; j < num; j++) {
Unspawn (array[j]);
}
}
#endregion
// ---- protected methods ----
#region protected methods
// Activates or deactivates the provided game object using the method
// specified by the setActiveRecursively flag.
protected void SetActive(GameObject target, bool value) {
if (setActiveRecursively) target.SetActiveRecursively(value);
else target.active = value;
}
#endregion
}
}
Как пользоваться. В скрипте где создаем пул объектов прописываем в начале
using pools;
Code
GameObjectPool bulletPull;
public GameObject bulletPrefab;
наши переменные, bulletPrefab хранит объект пули который будет загружен в сцену.
Code
void Start(){
bulletPull = new GameObjectPool(bulletPrefab,99,bulletHandler,true);
bulletPull.prePopulate(99);
}
void bulletHandler(GameObject target){
target.name = "bullet";
}
- Code
bulletPull = new GameObjectPool(bulletPrefab,99,bulletHandler,true);
= создаем новый пул
первый аргумент это игровой объект который надо создать, второй это начальное кол-во, третий аргумент запускает функцию для установки первоначальных данных для объектов если они есть, последний аргумент либо деактивирует только объект, либо объект и его дочерние объекты если таковы имеются.
Code
bulletPull.prePopulate(99);
- сразу создаем на сцене 99 объектов пуль.
Code
void bulletHandler(GameObject target){
target.name = "bullet";
}
Стартовая функция для созданных объектов пула - обязательно должна иметь аргумент игрового объекта ( это только что созданная пуля в нашем примере), собственно тут можно прописать все начальные параметры для объекта.
Все - теперь нам нужно правильно активировать объект и при необходимости возвращать его обратно в пул.
Code
bulletPull.Spawn (new Vector3 (0, 0, 0), Quaternion.identity);
- Spawn отвечает за активацию объекта из пула по позиции и вращению.
Возврат в пул выглядит так:
Code
bulletPull.Unspawn(myGameObject);
- где myGameObject - игровой объект который нужно вернуть обратно в конкретный (bulletPull) пул.
Автор (JS)