308 lines
8.9 KiB
C#
308 lines
8.9 KiB
C#
// =====================================================================
|
|
// Copyright 2013-2017 Fluffy Underware
|
|
// All rights reserved
|
|
//
|
|
// http://www.fluffyunderware.com
|
|
// =====================================================================
|
|
|
|
using UnityEngine;
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
using System.Collections;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using FluffyUnderware.DevTools.Extensions;
|
|
|
|
namespace FluffyUnderware.DevTools
|
|
{
|
|
/// <summary>
|
|
/// A pool of instances of a prefab GameObject
|
|
/// </summary>
|
|
[RequireComponent(typeof(PoolManager))]
|
|
[HelpURL(DTUtility.HelpUrlBase + "dtprefabpool")]
|
|
public class PrefabPool : DTVersionedMonoBehaviour, IPool
|
|
{
|
|
[FieldCondition(nameof(m_Identifier), "", false, ActionAttribute.ActionEnum.ShowWarning, "Please enter an identifier! (Select a prefab to set automatically)")]
|
|
[SerializeField]
|
|
string m_Identifier;
|
|
|
|
[SerializeField]
|
|
List<GameObject> m_Prefabs = new List<GameObject>();
|
|
|
|
[Inline]
|
|
[SerializeField]
|
|
PoolSettings m_Settings;
|
|
|
|
|
|
|
|
PoolManager mManager;
|
|
|
|
public string Identifier
|
|
{
|
|
get { return m_Identifier; }
|
|
set
|
|
{
|
|
if (m_Identifier != value)
|
|
{
|
|
string v = value;
|
|
if (string.IsNullOrEmpty(m_Identifier))
|
|
v = Manager.GetUniqueIdentifier(v);
|
|
m_Identifier = value;
|
|
}
|
|
}
|
|
|
|
}
|
|
public List<GameObject> Prefabs
|
|
{
|
|
get { return m_Prefabs; }
|
|
set
|
|
{
|
|
if (m_Prefabs != value)
|
|
m_Prefabs = value;
|
|
}
|
|
}
|
|
|
|
|
|
public PoolSettings Settings
|
|
{
|
|
get { return m_Settings; }
|
|
set
|
|
{
|
|
if (m_Settings != value)
|
|
m_Settings = value;
|
|
if (m_Settings != null)
|
|
m_Settings.OnValidate();
|
|
}
|
|
}
|
|
|
|
|
|
public PoolManager Manager
|
|
{
|
|
get
|
|
{
|
|
if (mManager == null)
|
|
mManager = GetComponent<PoolManager>();
|
|
return mManager;
|
|
}
|
|
}
|
|
|
|
List<GameObject> mObjects = new List<GameObject>();
|
|
|
|
double mLastTime;
|
|
double mDeltaTime;
|
|
|
|
void Start()
|
|
{
|
|
if (Settings.Prewarm)
|
|
Reset();
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
void OnValidate()
|
|
{
|
|
Settings = m_Settings;
|
|
}
|
|
#endif
|
|
|
|
public void Initialize(string ident, PoolSettings settings, params GameObject[] prefabs)
|
|
{
|
|
Identifier = ident;
|
|
m_Settings = settings;
|
|
Prefabs = new List<GameObject>(prefabs);
|
|
mLastTime = DTTime.TimeSinceStartup + UnityEngine.Random.Range(0, Settings.Speed);
|
|
if (Settings.Prewarm)
|
|
Reset();
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
mDeltaTime += DTTime.TimeSinceStartup - mLastTime;
|
|
mLastTime = DTTime.TimeSinceStartup;
|
|
|
|
if (Settings.Speed > 0)
|
|
{
|
|
int c = (int)(mDeltaTime / Settings.Speed);
|
|
mDeltaTime -= c;
|
|
|
|
if (Count > Settings.Threshold)
|
|
{
|
|
c = Mathf.Min(c, Count - Settings.Threshold);
|
|
while (c-- > 0)
|
|
{
|
|
if (Settings.Debug)
|
|
log("Threshold exceeded: Deleting item");
|
|
destroy(mObjects[0]);
|
|
mObjects.RemoveAt(0);
|
|
}
|
|
}
|
|
else if (Count < Settings.MinItems)
|
|
{
|
|
c = Mathf.Min(c, Settings.MinItems - Count);
|
|
while (c-- > 0)
|
|
{
|
|
if (Settings.Debug)
|
|
log("Below MinItems: Adding item");
|
|
mObjects.Add(create());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
mDeltaTime = 0;
|
|
}
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
while (Count < Settings.MinItems)
|
|
{
|
|
mObjects.Add(create());
|
|
}
|
|
while (Count > Settings.Threshold)
|
|
{
|
|
destroy(mObjects[0]);
|
|
mObjects.RemoveAt(0);
|
|
}
|
|
if (Settings.Debug)
|
|
log("Prewarm/Reset");
|
|
}
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
if (Settings.Debug)
|
|
log("Clear");
|
|
for (int i = 0; i < Count; i++)
|
|
destroy(mObjects[i]);
|
|
mObjects.Clear();
|
|
}
|
|
|
|
public int Count
|
|
{
|
|
get { return mObjects.Count; }
|
|
}
|
|
|
|
public GameObject Pop(Transform parent = null)
|
|
{
|
|
GameObject item = null;
|
|
if (Count > 0)
|
|
{
|
|
item = mObjects[0];
|
|
mObjects.RemoveAt(0);
|
|
}
|
|
else
|
|
{
|
|
if (Settings.AutoCreate || !Application.isPlaying)
|
|
{
|
|
if (Settings.Debug)
|
|
log("Auto create item");
|
|
item = create();
|
|
}
|
|
}
|
|
if (item)
|
|
{
|
|
item.gameObject.hideFlags = HideFlags.None;
|
|
item.transform.parent = parent;
|
|
if (Settings.AutoEnableDisable)
|
|
item.SetActive(true);
|
|
sendAfterPop(item);
|
|
if (Settings.Debug)
|
|
log("Pop " + item);
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
public virtual void Push(GameObject item)
|
|
{
|
|
if (Settings.Debug)
|
|
log("Push " + item);
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
item.gameObject.Destroy(false, true);
|
|
}
|
|
else
|
|
#endif
|
|
if (item != null)
|
|
{
|
|
sendBeforePush(item);
|
|
mObjects.Add(item);
|
|
item.transform.parent = transform;
|
|
item.gameObject.hideFlags = (Settings.Debug) ? HideFlags.DontSave : HideFlags.HideAndDontSave;
|
|
if (Settings.AutoEnableDisable)
|
|
item.SetActive(false);
|
|
}
|
|
}
|
|
|
|
GameObject create()
|
|
{
|
|
if (Prefabs.Count == 0)
|
|
throw new InvalidOperationException(String.Format("[Curvy] The Prefab Pool '{0}' in game object '{1}' could not create a pool element because its Prefabs list is empty", this.Identifier, this.gameObject.name));
|
|
|
|
//TODO should this Random.Range call be deterministic?
|
|
GameObject prefab = Prefabs[UnityEngine.Random.Range(0, Prefabs.Count)];
|
|
GameObject result;
|
|
{
|
|
#if UNITY_EDITOR
|
|
bool isPrefabAsset;
|
|
//We have to check GetPrefabInstanceStatus first, because GetPrefabAssetType returns (as far as I understand) the same thing for both a prefab asset and a prefab instance
|
|
if (PrefabUtility.GetPrefabInstanceStatus(prefab) == PrefabInstanceStatus.NotAPrefab)
|
|
{
|
|
PrefabAssetType prefabAssetType = PrefabUtility.GetPrefabAssetType(prefab);
|
|
isPrefabAsset = prefabAssetType == PrefabAssetType.Regular || prefabAssetType == PrefabAssetType.Variant;
|
|
}
|
|
else
|
|
isPrefabAsset = false;
|
|
|
|
result = isPrefabAsset
|
|
? PrefabUtility.InstantiatePrefab(prefab) as GameObject
|
|
: Instantiate(prefab);
|
|
#else
|
|
result = Instantiate(prefab);
|
|
#endif
|
|
}
|
|
|
|
result.name = prefab.name;
|
|
result.transform.parent = transform;
|
|
if (Settings.AutoEnableDisable)
|
|
result.SetActive(false);
|
|
return result;
|
|
}
|
|
|
|
void destroy(GameObject go)
|
|
{
|
|
go.Destroy(false, true);
|
|
}
|
|
|
|
void log(string msg)
|
|
{
|
|
Debug.Log(string.Format("[{0}] ({1} items) {2}", Identifier, Count, msg));
|
|
}
|
|
|
|
void setParent(Transform item, Transform parent)
|
|
{
|
|
if (item != null)
|
|
item.parent = parent;
|
|
}
|
|
|
|
void sendAfterPop(GameObject item)
|
|
{
|
|
item.SendMessage(nameof(IPoolable.OnAfterPop), SendMessageOptions.DontRequireReceiver);
|
|
}
|
|
|
|
void sendBeforePush(GameObject item)
|
|
{
|
|
item.SendMessage(nameof(IPoolable.OnBeforePush), SendMessageOptions.DontRequireReceiver);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|