Pull to refresh

Comments 10

Следующий класс PoolManager управляет пулами различных объектов. Класс статический для упрощения доступа к объектам, т.е. не нужно создавать синглтоны, инстансы и прочее.

Как решается то, что статик класс содержит линки на мертвые GameObject-ы при смене сцены? Нужно или чистить или просто применять локальный для сцены пул (который может быть как синглтоном, так и просто отдельным компонентом со ссылкой на него), который будет умирать самостоятельно.
Да, резонное замечание, надо дописать. Просто в моем проекте сцена, где нужен пул, всего одна и очистка не требуется. Вообще при смене сцены и переинициализации от PoolSetup в objects запишется новое значение. Но необходимости очистки при смене сцен это не отменяет, согласен.
Ну и если хочется универсальности, то можно прикрутить загрузку префабов из ресурсов:
    public sealed class PoolObject : MonoBehaviour {
        public PoolContainer Pool { get; private set; }

        public Transform CachedTransform { get; private set; }

        void Awake () {
            CachedTransform = transform;
        }

        public void SetPool (PoolContainer pool) {
            Pool = pool;
        }

        public void Recycle () {
            if (Pool != null) {
                Pool.Recycle (this);
            }
        }

        public void SetActive (bool state) {
            gameObject.SetActive (state);
        }
    }

public sealed class PoolContainer : MonoBehaviour {
        readonly Stack<PoolObject> _store = new Stack<PoolObject> (64);

        GameObject _prefab;

        public string PrefabPath = "UnknownPrefab";

        bool LoadPrefab () {
            _prefab = Resources.Load<GameObject> (PrefabPath);
            if (_prefab == null) {
                Debug.LogWarning ("Cant load asset " + PrefabPath);
                return false;
            }
            #if UNITY_EDITOR
            if (_prefab.GetComponent <PoolObject> () != null) {
                Debug.LogWarning ("PoolObject cant be used on prefabs");
                _prefab = null;
                UnityEditor.EditorApplication.isPaused = true;
                return false;
            }
            #endif

            return true;
        }

        public PoolObject Get () {
            if (_prefab == null) {
                if (!LoadPrefab ()) {
                    return null;
                }
            }

            PoolObject obj;
            if (_store.Count > 0) {
                obj = _store.Pop ();
            } else {
                var go = Instantiate<GameObject> (_prefab);
                obj = go.AddComponent<PoolObject> ();
                obj.SetPool (this);
            }
            obj.SetActive (false);
            return obj;
        }

        public void Recycle (PoolObject obj) {
            if (obj != null && obj.Pool == this) {
                obj.SetActive (false);
                if (!_store.Contains (obj)) {
                    _store.Push (obj);
                }
            } else {
                #if UNITY_EDITOR
                Debug.LogWarning ("Invalid obj to recycle", obj);
                #endif
            }
        }
    }

Вот и весь пулинг. В сцене делается ГО с пул-контейнером + настройкой, а в компоненте, где требуется инстанцировать объекты, делается паблик свойство с типом PoolContainer, в которое потом перетаскивается созданный ГО в инспекторе визуально (дизайнер оценит).
Можно делать всякие защитные меры или специальное поведение:
public sealed class RecycleAfterTime : MonoBehaviour {
        public float Timeout = 1f;

        float _endTime;

        PoolObject _poolObject;

        void OnEnable () {
            _endTime = Time.time + Timeout;
        }

        void LateUpdate () {
            if (Time.time >= _endTime) {
                OnRecycle ();
            }
        }

        void OnRecycle () {
            var poolObj = GetComponent <PoolObject> ();
            if (poolObj != null) {
                poolObj.Recycle ();
            } else {
                gameObject.SetActive (false);
            }
        }
    }
Вам стоит добавить кеширование получения компонентов.
Ну по сути тут только transform два раза подряд вызывается, потому и не заморачивался. Но да, надо исправить.
Я видел много вызовов getComponent
Скорее всего, вы про эти строки:
		objects.Add(temp.GetComponent<PoolObject> ());
		if (temp.GetComponent<Animator> ())
			temp.GetComponent<Animator> ().StartPlayback ();


Тут можно закэшировать аниматор. А PoolObject кастуется лишь раз. AddObject для любого из объектов пула вызывается лишь при создании.
1. Как на счет того, чтобы сделать размер пула? В большинстве случаев объектов внутри этого размера должно хватать для сцены. Если иногда возникает ситуация, что нужно создать чуть больше объектов, то пул создает их и уничтожает при последующем освобождении, вновь оставляя в пуле только обычное количество объектов.
2. Может быть имеет смысл инициализировать все пулы с заданным количеством объектов перед загрузкой уровня, чтобы не возникало скачков FPS при создании объектов во время игрового цикла? Т.е. пул не досоздает объекты по мере необходимости, а перед стартом уровня создает их с запасом и потом выдает только готовые.
1. Так и делается (переменная count в структуре PoolPart класса PoolManager), но я не вводил уничтожение объектов, т.к. если вам единожды понадобилось 300 пуль вместо 30, то есть вероятность, что понадобится снова, но они уже созданы.
2. Так все и происходит, досоздание объектов происходит только тогда, когда все объекты в пуле уже выданы «на руки».
А что если это будут не пули, а тяжеловесные объекты? Предположим, у нас генерится случайный мир и вероятность такая, что обычно каждый вид объекта попадает на сцену в количестве 10 штук. Но иногда случайно все же может быть сгенерировано 20 одинаковых штук. В этом случае пул досоздаст 10 объектов. Если их не уничтожить, то так и будем их таскать всю игру, даже если больше никогда не сгенерится 20 штук. Опять же, такая ситуация может случится и с другими видами объектов. В итоге получим, что вместо 10 штук на каждый вид, будет храниться 20 — т.е. памяти потребуется вдвое больше. А еще один плохой момент, это что вначале игры памяти будет хватать, а в процессе она будет захватываться. Поэтому я бы может быть даже ввел счетчик и подстраивал размер пула под текущие нужды
Sign up to leave a comment.

Articles

Change theme settings