PoolableObject.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014
  1. /*************************************************************
  2. * Unity Object Pool (c) by ClockStone 2011 *
  3. *
  4. * Allows to "pool" prefab objects to avoid large number of
  5. * Instantiate() calls.
  6. *
  7. * Usage:
  8. *
  9. * Add the PoolableObject script component to the prefab to be pooled.
  10. * You can set the maximum number of objects to be be stored in the
  11. * pool from within the inspector.
  12. *
  13. * Replace all Instantiate( myPrefab ) calls with
  14. * ObjectPoolController.Instantiate( myPrefab)
  15. *
  16. * Replace all Destroy( myObjectInstance ) calls with
  17. * ObjectPoolController.Destroy( myObjectInstance )
  18. *
  19. * Note that Awake(), and OnDestroy() get called correctly for
  20. * pooled objects. However, make sure that all component data that could
  21. * possibly get changed during its lifetime get reinitialized by the
  22. * Awake() function.
  23. * The Start() function gets also called, but just after the Awake() function
  24. * during ObjectPoolController.Instantiate(...)
  25. *
  26. * If a poolable objects gets parented to none-poolable object, the parent must
  27. * be destroyed using ObjectPoolController.Destroy( ... )
  28. *
  29. * Be aware that OnDestroy() will get called multiple times:
  30. * a) the time ObjectPoolController.Destroy() is called when the object is added
  31. * to the pool
  32. * b) when the object really gets destroyed (e.g. if a new scene is loaded)
  33. *
  34. * References to pooled objects will not change to null anymore once an object has
  35. * been "destroyed" and moved to the pool. Use PoolableReference if you need such checks.
  36. *
  37. * ********************************************************************
  38. */
  39. #if UNITY_3_5 || UNITY_3_4 || UNITY_3_3 || UNITY_3_2 || UNITY_3_1 || UNITY_3_0
  40. #define UNITY_3_x
  41. #endif
  42. #if UNITY_FLASH || UNITY_WINRT
  43. #define REDUCED_REFLECTION
  44. #endif
  45. using UnityEngine;
  46. using System.Collections;
  47. using System.Collections.Generic;
  48. using System.Linq;
  49. using System;
  50. using System.Reflection;
  51. #pragma warning disable 1591 // undocumented XML code warning
  52. /// <summary>
  53. /// Add this component to your prefab to make it poolable.
  54. /// </summary>
  55. /// <remarks>
  56. /// See <see cref="ObjectPoolController"/> for an explanation how to set up a prefab for pooling.
  57. /// The following messages are sent to a poolable object:
  58. /// <list type="bullet">
  59. /// <item>
  60. /// <c>Awake</c> and <c>OnDestroy</c> whenever a poolable object is activated or deactivated from the pool.
  61. /// This way the same behaviour is simulated as if the object was instantiated respectively destroyed.
  62. /// These messages are only sent when <see cref="sendAwakeStartOnDestroyMessage"/> is enabled.
  63. /// </item>
  64. /// <item>
  65. /// <c>OnPoolableInstanceAwake</c> and <c>OnPoolableInstanceDestroy</c> when the object was actually instantiated respectively destroyed.
  66. /// Because of current Unity limitations <c>OnPoolableInstanceDestroy</c> does not work on Flash!
  67. /// </item>
  68. /// /// <item>
  69. /// <c>OnPoolableObjectActivated</c> and <c>OnPoolableObjectDeactivated</c> whenever a poolable object is activated or deactivated from the pool.
  70. /// These messages are only sent when <see cref="sendPoolableActivateDeactivateMessages"/> is enabled.
  71. /// </item>
  72. /// </list>
  73. /// </remarks>
  74. /// <seealso cref="ObjectPoolController"/>
  75. [AddComponentMenu( "ClockStone/PoolableObject" )]
  76. public class PoolableObject : MonoBehaviour
  77. {
  78. /// <summary>
  79. /// The maximum number of instances of this prefab to get stored in the pool.
  80. /// </summary>
  81. public int maxPoolSize = 10;
  82. /// <summary>
  83. /// This number of instances will be preloaded to the pool if <see cref="ObjectPoolController.Preload(GameObject)"/> is called.
  84. /// </summary>
  85. public int preloadCount = 0;
  86. /// <summary>
  87. /// If enabled the object will not get destroyed if a new scene is loaded
  88. /// </summary>
  89. public bool doNotDestroyOnLoad = false;
  90. /// <summary>
  91. /// If enabled Awake(), Start(), and OnDestroy() messages are sent to the poolable object if the object is set
  92. /// active respectively inactive whenever <see cref="ObjectPoolController.Destroy(GameObject)"/> or
  93. /// <see cref="ObjectPoolController.Instantiate(GameObject)"/> is called. <para/>
  94. /// This way it is simulated that the object really gets instantiated respectively destroyed.
  95. /// </summary>
  96. /// <remarks>
  97. /// The Start() function is called immedialtely after Awake() by <see cref="ObjectPoolController.Instantiate(GameObject)"/>
  98. /// and not next frame. So do not set data after <see cref="ObjectPoolController.Instantiate(GameObject)"/> that Start()
  99. /// relies on. In some cases you may not want the Awake(), Start(), and OnDestroy() messages to be sent for performance
  100. /// reasons because it may not be necessary to fully reinitialize a game object each time it is activated from the pool.
  101. /// You can still use the <c>OnPoolableObjectActivated</c> and <c>OnPoolableObjectDeactivated</c> messages to initialize
  102. /// specific data.
  103. /// </remarks>
  104. public bool sendAwakeStartOnDestroyMessage = true;
  105. /// <summary>
  106. /// If enabled a <c>OnPoolableObjectActivated</c> and <c>OnPoolableObjectDeactivated</c> message is sent to
  107. /// the poolable instance if the object is activated respectively deactivated by the <see cref="ObjectPoolController"/>
  108. /// </summary>
  109. public bool sendPoolableActivateDeactivateMessages = false;
  110. internal bool _isAvailableForPooling = false;
  111. internal bool _createdWithPoolController = false;
  112. internal bool _destroyMessageFromPoolController = false;
  113. internal bool _wasPreloaded = false;
  114. internal bool _wasStartCalledByUnity = false;
  115. internal ObjectPoolController.ObjectPool _myPool = null;
  116. internal int _serialNumber = 0;
  117. internal int _usageCount = 0;
  118. #if UNITY_EDITOR
  119. protected void Awake()
  120. {
  121. //Debug.Log( string.Format( "Awake: {0} Pool:{1}", name, _myPool != null ) );
  122. if ( _myPool == null && !ObjectPoolController._isDuringInstantiate )
  123. {
  124. Debug.LogWarning( "Poolable object " + name + " was instantiated without ObjectPoolController" );
  125. }
  126. }
  127. #endif
  128. protected void Start()
  129. {
  130. _wasStartCalledByUnity = true;
  131. }
  132. #if !REDUCED_REFLECTION
  133. private static void _InvokeMethodByName( MonoBehaviour behaviour, string methodName )
  134. {
  135. // Get the Type for the class
  136. Type calledType = behaviour.GetType();
  137. var methodInfo = calledType.GetMethod( methodName,
  138. BindingFlags.Instance | BindingFlags.NonPublic );
  139. if ( methodInfo != null )
  140. {
  141. methodInfo.Invoke( behaviour, null );
  142. }
  143. }
  144. #endif
  145. // broadcasts also to inactive game objects, does not broadcast to poolable child objects
  146. static private void _BroadcastMessageToGameObject( GameObject go, string message )
  147. {
  148. #if !REDUCED_REFLECTION
  149. var components = go.GetComponents( typeof( MonoBehaviour ) );
  150. foreach ( var c in components )
  151. {
  152. _InvokeMethodByName( ( MonoBehaviour ) c, message );
  153. }
  154. if ( go.transform.childCount > 0 )
  155. {
  156. _BroadcastMessageToAllChildren( go, message );
  157. }
  158. #else
  159. go.BroadcastMessage( message, SendMessageOptions.DontRequireReceiver ); // workaround for Flash - does not work for OnPoolableInstanceDestroy!!
  160. #endif
  161. }
  162. private static void _BroadcastMessageToAllChildren( GameObject go, string message )
  163. {
  164. Transform[] children = new Transform[ go.transform.childCount ]; // save child array, as it may change
  165. for ( int i = 0; i < go.transform.childCount; i++ )
  166. children[ i ] = go.transform.GetChild( i );
  167. //now recursively do the same for all children
  168. for ( int i = 0; i < children.Length; i++ )
  169. {
  170. if ( children[ i ].GetComponent<PoolableObject>() == null ) // if child object is PoolableObject then don't broadcast
  171. {
  172. PoolableObject._BroadcastMessageToGameObject( children[ i ].gameObject, message );
  173. }
  174. }
  175. }
  176. protected void OnDestroy()
  177. {
  178. if ( !_destroyMessageFromPoolController && _myPool != null )
  179. {
  180. // Poolable object was destroyed by using the default Unity Destroy() function -> Use ObjectPoolController.Destroy() instead
  181. // This can also happen if objects are automatically deleted by Unity e.g. due to level change or if an object is parented to an object that gets destroyed
  182. _myPool.Remove( this );
  183. //Debug.LogError( "Destroy S/N:" + _serialNumber );
  184. }
  185. if ( !_destroyMessageFromPoolController )
  186. {
  187. // can't use Unity's BroadcastMessage because it doesn't send to object that are just being destroyed
  188. _BroadcastMessageToGameObject( gameObject, "OnPoolableInstanceDestroy" );
  189. }
  190. _destroyMessageFromPoolController = false;
  191. }
  192. /// <summary>
  193. /// Gets the object's pool serial number. Each object has a unique serial number. Can be useful for debugging purposes.
  194. /// </summary>
  195. /// <returns>
  196. /// The serial number (starting with 1 for each pool).
  197. /// </returns>
  198. public int GetSerialNumber() // each new instance receives a unique serial number
  199. {
  200. return _serialNumber;
  201. }
  202. /// <summary>
  203. /// Gets the usage counter which gets increased each time an object is re-used from the pool.
  204. /// </summary>
  205. /// <returns>
  206. /// The usage counter
  207. /// </returns>
  208. public int GetUsageCount()
  209. {
  210. return _usageCount;
  211. }
  212. /// <summary>
  213. /// Moves all poolable objects of this kind (instantiated from the same prefab as this instance) back to the pool.
  214. /// </summary>
  215. /// <returns>
  216. /// The number of instances deactivated and moved back to its pool.
  217. /// </returns>
  218. public int DeactivateAllPoolableObjectsOfMyKind()
  219. {
  220. if ( _myPool != null )
  221. {
  222. return _myPool._SetAllAvailable();
  223. }
  224. return 0;
  225. }
  226. /// <summary>
  227. /// Checks if the object is deactivated and in the pool.
  228. /// </summary>
  229. /// <returns>
  230. /// <c>true</c> if the object is in the pool of deactivated objects, otherwise <c>false</c>.
  231. /// </returns>
  232. public bool IsDeactivated()
  233. {
  234. return _isAvailableForPooling;
  235. }
  236. /// <summary>
  237. /// Retrieves an array of all poolable objects of this kind (instantiated from the same prefab as this instance).
  238. /// </summary>
  239. /// <param name="includeInactiveObjects">
  240. /// If enabled, the returned array will also include the inactive objects in the pool.
  241. /// </param>
  242. /// <returns>
  243. /// The array of poolable objects.
  244. /// </returns>
  245. public PoolableObject[] GetAllPoolableObjectsOfMyKind( bool includeInactiveObjects )
  246. {
  247. if ( _myPool != null )
  248. {
  249. return _myPool._GetAllObjects( includeInactiveObjects );
  250. }
  251. return null;
  252. }
  253. }
  254. /// <summary>
  255. /// A static class used to create and destroy poolable objects.
  256. /// </summary>
  257. /// <remarks>
  258. /// What is pooling? <para/>
  259. /// GameObject.Instantiate(...) calls are relatively time expensive. If objects of the same
  260. /// type are frequently created and destroyed it is good practice to use object pools, particularly on mobile
  261. /// devices. This can greatly reduce the performance impact for object creation and garbage collection. <para/>
  262. /// How does pooling work?<para/>
  263. /// Instead of actually destroying object instances, they are just set inactive and moved to an object "pool".
  264. /// If a new object is requested it can then simply be pulled from the pool, instead of creating a new instance. <para/>
  265. /// Awake(), Start() and OnDestroy() are called if objects are retrieved from or moved to the pool like they
  266. /// were instantiated and destroyed normally.
  267. /// </remarks>
  268. /// <example>
  269. /// How to set up a prefab for pooling:
  270. /// <list type="number">
  271. /// <item>Add the PoolableObject script component to the prefab to be pooled.
  272. /// You can set the maximum number of objects to be be stored in the pool from within the inspector.</item>
  273. /// <item> Replace all <c>Instantiate( myPrefab )</c> calls with <c>ObjectPoolController.Instantiate( myPrefab )</c></item>
  274. /// <item> Replace all <c>Destroy( myObjectInstance )</c> calls with <c>ObjectPoolController.Destroy( myObjectInstance )</c></item>
  275. /// </list>
  276. /// Attention: Be aware that:
  277. /// <list type="bullet">
  278. /// <item>All data must get initialized in the Awake() or Start() function</item>
  279. /// <item><c>OnDestroy()</c> will get called a second time once the object really gets destroyed by Unity</item>
  280. /// <item>If a poolable objects gets parented to none-poolable object, the parent must
  281. /// be destroyed using <c>ObjectPoolController.Destroy( ... )</c> even if it is none-poolable itself.</item>
  282. /// <item>If you store a reference to a poolable object then this reference does not evaluate to <c>null</c> after <c>ObjectPoolController.Destroy( ... )</c>
  283. /// was called like other references to Unity objects normally would. This is because the object still exists - it is just in the pool.
  284. /// To make sure that a stored reference to a poolable object is still valid you must use <see cref="PoolableReference{T}"/>.</item>
  285. /// </list>
  286. /// </example>
  287. /// <seealso cref="PoolableObject"/>
  288. static public class ObjectPoolController
  289. {
  290. static public bool isDuringPreload
  291. {
  292. get;
  293. private set;
  294. }
  295. // **************************************************************************************************/
  296. // public functions
  297. // **************************************************************************************************/
  298. /// <summary>
  299. /// Retrieves an instance of the specified prefab. Either returns a new instance or it claims an instance
  300. /// from the pool.
  301. /// </summary>
  302. /// <param name="prefab">The prefab to be instantiated.</param>
  303. /// <returns>
  304. /// An instance of the prefab.
  305. /// </returns>
  306. /// <remarks>
  307. /// Can be used on none-poolable objects as well. It is good practice to use <c>ObjectPoolController.Instantiate</c>
  308. /// whenever you may possibly make your prefab poolable in the future.
  309. /// </remarks>
  310. /// <seealso cref="Destroy(GameObject)"/>
  311. static public GameObject Instantiate( GameObject prefab )
  312. {
  313. PoolableObject prefabPool = prefab.GetComponent<PoolableObject>();
  314. if ( prefabPool == null )
  315. {
  316. //Debug.LogWarning( "Object " + prefab.name + " not poolable " );
  317. return ( GameObject ) GameObject.Instantiate( prefab ); // prefab not pooled, instantiate normally
  318. }
  319. GameObject go = _GetPool( prefabPool ).GetPooledInstance( null, null );
  320. if ( go )
  321. {
  322. return go;
  323. }
  324. else // pool is full
  325. {
  326. return InstantiateWithoutPool( prefab );
  327. }
  328. }
  329. /// <summary>
  330. /// Retrieves an instance of the specified prefab. Either returns a new instance or it claims an instance
  331. /// from the pool.
  332. /// </summary>
  333. /// <param name="prefab">The prefab to be instantiated.</param>
  334. /// <param name="position">The position in world coordinates.</param>
  335. /// <param name="quaternion">The rotation quaternion.</param>
  336. /// <returns>
  337. /// An instance of the prefab.
  338. /// </returns>
  339. /// <remarks>
  340. /// Can be used on none-poolable objects as well. It is good practice to use <c>ObjectPoolController.Instantiate</c>
  341. /// whenever you may possibly make your prefab poolable in the future.
  342. /// </remarks>
  343. /// <seealso cref="Destroy(GameObject)"/>
  344. static public GameObject Instantiate( GameObject prefab, Vector3 position, Quaternion quaternion )
  345. {
  346. PoolableObject prefabPool = prefab.GetComponent<PoolableObject>();
  347. if ( prefabPool == null )
  348. {
  349. // no warning displayed by design because this allows to decide later if the object will be poolable or not
  350. // Debug.LogWarning( "Object " + prefab.name + " not poolable ");
  351. return ( GameObject ) GameObject.Instantiate( prefab, position, quaternion ); // prefab not pooled, instantiate normally
  352. }
  353. GameObject go = _GetPool( prefabPool ).GetPooledInstance( position, quaternion );
  354. if ( go )
  355. {
  356. return go;
  357. }
  358. else // pool is full
  359. {
  360. //Debug.LogWarning( "Pool Full" );
  361. return InstantiateWithoutPool( prefab, position, quaternion );
  362. }
  363. }
  364. /// <summary>
  365. /// Instantiates the specified prefab without using pooling.
  366. /// from the pool.
  367. /// </summary>
  368. /// <param name="prefab">The prefab to be instantiated.</param>
  369. /// <returns>
  370. /// An instance of the prefab.
  371. /// </returns>
  372. /// <remarks>
  373. /// If the prefab is poolable, the <see cref="PoolableObject"/> component will be removed.
  374. /// This way no warning is generated that a poolable object was created without pooling.
  375. /// </remarks>
  376. static public GameObject InstantiateWithoutPool( GameObject prefab )
  377. {
  378. return InstantiateWithoutPool( prefab, new Vector3( 0, 0, 0 ), Quaternion.identity );
  379. }
  380. /// <summary>
  381. /// Instantiates the specified prefab without using pooling.
  382. /// from the pool.
  383. /// </summary>
  384. /// <param name="prefab">The prefab to be instantiated.</param>
  385. /// <param name="position">The position in world coordinates.</param>
  386. /// <param name="quaternion">The rotation quaternion.</param>
  387. /// <returns>
  388. /// An instance of the prefab.
  389. /// </returns>
  390. /// <remarks>
  391. /// If the prefab is poolable, the <see cref="PoolableObject"/> component will be removed.
  392. /// This way no warning is generated that a poolable object was created without pooling.
  393. /// </remarks>
  394. static public GameObject InstantiateWithoutPool( GameObject prefab, Vector3 position, Quaternion quaternion )
  395. {
  396. GameObject go;
  397. _isDuringInstantiate = true;
  398. go = ( GameObject ) GameObject.Instantiate( prefab, position, quaternion ); // prefab not pooled, instantiate normally
  399. _isDuringInstantiate = false;
  400. PoolableObject pool = go.GetComponent<PoolableObject>();
  401. if ( pool )
  402. {
  403. if ( pool.doNotDestroyOnLoad )
  404. {
  405. GameObject.DontDestroyOnLoad( go );
  406. }
  407. pool._createdWithPoolController = true; // so no warning is displayed if object gets ObjectPoolCOntroller.Destroy()-ed before the component actually gets removed
  408. Component.Destroy( pool );
  409. }
  410. return go;
  411. }
  412. /// <summary>
  413. /// Destroys the specified game object, respectively sets the object inactive and adds it to the pool.
  414. /// </summary>
  415. /// <param name="obj">The game object.</param>
  416. /// <remarks>
  417. /// Can be used on none-poolable objects as well. It is good practice to use <c>ObjectPoolController.Destroy</c>
  418. /// whenever you may possibly make your prefab poolable in the future. <para/>
  419. /// Must also be used on none-poolable objects with poolable child objects so the poolable child objects are correctly
  420. /// moved to the pool.
  421. /// </remarks>
  422. /// <seealso cref="Instantiate(GameObject)"/>
  423. static public void Destroy( GameObject obj ) // destroys poolable and none-poolable objects. Destroys poolable children correctly
  424. {
  425. PoolableObject poolObj = obj.GetComponent<PoolableObject>();
  426. if ( poolObj == null )
  427. {
  428. _DetachChildrenAndDestroy( obj.transform ); // child objects may be poolable
  429. GameObject.Destroy( obj ); // prefab not pooled, destroy normally
  430. return;
  431. }
  432. if ( poolObj._myPool != null )
  433. {
  434. poolObj._myPool._SetAvailable( poolObj, true );
  435. }
  436. else
  437. {
  438. if ( !poolObj._createdWithPoolController )
  439. {
  440. Debug.LogWarning( "Poolable object " + obj.name + " not created with ObjectPoolController" );
  441. }
  442. GameObject.Destroy( obj ); // prefab not pooled, destroy normally
  443. }
  444. }
  445. /// <summary>
  446. /// Preloads as many instances to the pool so that there are at least as many as
  447. /// specified in <see cref="PoolableObject.preloadCount"/>.
  448. /// </summary>
  449. /// <param name="prefab">The prefab.</param>
  450. /// <remarks>
  451. /// Use ObjectPoolController.isDuringPreload to check if an object is preloaded in the <c>Awake()</c> function.
  452. /// If the pool already contains at least <see cref="PoolableObject.preloadCount"/> objects, the function does nothing.
  453. /// </remarks>
  454. /// <seealso cref="PoolableObject.preloadCount"/>
  455. static public void Preload( GameObject prefab ) // adds as many instances to the prefab pool as specified in the PoolableObject
  456. {
  457. PoolableObject poolObj = prefab.GetComponent<PoolableObject>();
  458. if ( poolObj == null )
  459. {
  460. Debug.LogWarning( "Can not preload because prefab '" + prefab.name + "' is not poolable" );
  461. return;
  462. }
  463. var pool = _GetPool( poolObj ); // _preloadDone is set by _GetPool
  464. int delta = poolObj.preloadCount - pool.GetObjectCount();
  465. if ( delta <= 0 )
  466. {
  467. return;
  468. }
  469. isDuringPreload = true;
  470. try
  471. {
  472. for ( int i = 0; i < delta; i++ )
  473. {
  474. pool.PreloadInstance();
  475. }
  476. }
  477. finally
  478. {
  479. isDuringPreload = false;
  480. }
  481. //Debug.Log( "preloaded: " + prefab.name + " " + poolObj.preloadCount + " times" );
  482. }
  483. // **************************************************************************************************/
  484. // protected / private functions
  485. // **************************************************************************************************/
  486. internal static int _globalSerialNumber = 0;
  487. internal static bool _isDuringInstantiate = false;
  488. internal class ObjectPool
  489. {
  490. HashSet_Flash<PoolableObject> _pool;
  491. PoolableObject _prefabPoolObj;
  492. Transform _poolParentDummy;
  493. internal Transform poolParentDummy
  494. {
  495. get
  496. {
  497. _ValidatePoolParentDummy();
  498. return _poolParentDummy;
  499. }
  500. }
  501. private void _ValidatePoolParentDummy()
  502. {
  503. if ( !_poolParentDummy )
  504. {
  505. var poolParentDummyGameObject = new GameObject( "POOL:" + _prefabPoolObj.name );
  506. _poolParentDummy = poolParentDummyGameObject.transform;
  507. _SetActive( poolParentDummyGameObject, false );
  508. if ( _prefabPoolObj.doNotDestroyOnLoad )
  509. {
  510. GameObject.DontDestroyOnLoad( poolParentDummyGameObject );
  511. }
  512. }
  513. }
  514. public ObjectPool( GameObject prefab )
  515. {
  516. _prefabPoolObj = prefab.GetComponent<PoolableObject>();
  517. }
  518. private void _ValidatePooledObjectDataContainer()
  519. {
  520. if ( _pool == null )
  521. {
  522. _pool = new HashSet_Flash<PoolableObject>();
  523. _ValidatePoolParentDummy();
  524. }
  525. }
  526. internal void Remove( PoolableObject poolObj )
  527. {
  528. _pool.Remove( poolObj );
  529. }
  530. internal int GetObjectCount()
  531. {
  532. if ( _pool == null ) return 0;
  533. return _pool.Count;
  534. }
  535. internal GameObject GetPooledInstance( Vector3? position, Quaternion? rotation )
  536. {
  537. _ValidatePooledObjectDataContainer();
  538. Transform prefabTransform = _prefabPoolObj.transform;
  539. Transform objTransform;
  540. foreach ( PoolableObject o in _pool )
  541. {
  542. if ( o != null && o._isAvailableForPooling )
  543. {
  544. objTransform = o.transform;
  545. objTransform.position = ( position != null ) ? ( Vector3 ) position : prefabTransform.position;
  546. objTransform.rotation = ( rotation != null ) ? ( Quaternion ) rotation : prefabTransform.rotation;
  547. objTransform.localScale = prefabTransform.localScale;
  548. o._usageCount++;
  549. _SetAvailable( o, false );
  550. return o.gameObject;
  551. }
  552. }
  553. if ( _pool.Count < _prefabPoolObj.maxPoolSize ) // add new element to pool
  554. {
  555. return _NewPooledInstance( position, rotation ).gameObject;
  556. }
  557. // pool is full
  558. return null;
  559. }
  560. internal PoolableObject PreloadInstance()
  561. {
  562. _ValidatePooledObjectDataContainer();
  563. PoolableObject poolObj = _NewPooledInstance( null, null );
  564. poolObj._wasPreloaded = true;
  565. _SetAvailable( poolObj, true );
  566. return poolObj;
  567. }
  568. private PoolableObject _NewPooledInstance( Vector3? position, Quaternion? rotation )
  569. {
  570. GameObject go;
  571. _isDuringInstantiate = true;
  572. if ( position != null && rotation != null )
  573. {
  574. go = ( GameObject ) GameObject.Instantiate( _prefabPoolObj.gameObject, ( Vector3 ) position, ( Quaternion ) rotation );
  575. }
  576. else
  577. {
  578. go = ( GameObject ) GameObject.Instantiate( _prefabPoolObj.gameObject );
  579. }
  580. _isDuringInstantiate = false;
  581. PoolableObject poolObj = go.GetComponent<PoolableObject>();
  582. poolObj._createdWithPoolController = true;
  583. poolObj._myPool = this;
  584. poolObj._isAvailableForPooling = false;
  585. poolObj._serialNumber = ++_globalSerialNumber;
  586. poolObj._usageCount++;
  587. if ( poolObj.doNotDestroyOnLoad )
  588. {
  589. GameObject.DontDestroyOnLoad( go );
  590. }
  591. _pool.Add( poolObj );
  592. go.BroadcastMessage( "OnPoolableInstanceAwake", SendMessageOptions.DontRequireReceiver );
  593. return poolObj;
  594. }
  595. /// <summary>
  596. /// Deactivate all active pooled objects
  597. /// </summary>
  598. internal int _SetAllAvailable()
  599. {
  600. int count = 0;
  601. foreach ( PoolableObject o in _pool )
  602. {
  603. if ( o != null && !o._isAvailableForPooling )
  604. {
  605. _SetAvailable( o, true );
  606. count++;
  607. }
  608. }
  609. return count;
  610. }
  611. internal PoolableObject[] _GetAllObjects( bool includeInactiveObjects )
  612. {
  613. var list = new List<PoolableObject>();
  614. foreach ( PoolableObject o in _pool )
  615. {
  616. if ( o != null )
  617. {
  618. if ( includeInactiveObjects || !o._isAvailableForPooling )
  619. {
  620. list.Add( o );
  621. }
  622. }
  623. }
  624. return list.ToArray();
  625. }
  626. internal void _SetAvailable( PoolableObject poolObj, bool b )
  627. {
  628. poolObj._isAvailableForPooling = b;
  629. var objTransform = poolObj.transform;
  630. if ( b )
  631. {
  632. if ( poolObj.sendAwakeStartOnDestroyMessage )
  633. {
  634. poolObj._destroyMessageFromPoolController = true;
  635. }
  636. objTransform.parent = null; // object could still be parented, so detach
  637. _RecursivelySetInactiveAndSendMessages( poolObj.gameObject, poolObj, false );
  638. objTransform.parent = poolObj._myPool.poolParentDummy; // attach to dummy Parent
  639. //poolObj.gameObject.name = "pooled:" + poolObj._myPool._prefabPoolObj.name;
  640. }
  641. else
  642. {
  643. objTransform.parent = null; // detach from poolParentDummy
  644. _SetActiveAndSendMessages( poolObj.gameObject, poolObj );
  645. //poolObj.gameObject.name = poolObj._myPool._prefabPoolObj.name;
  646. }
  647. }
  648. private void _SetActive( GameObject obj, bool active )
  649. {
  650. #if UNITY_3_x
  651. if ( active )
  652. {
  653. obj.SetActiveRecursively( true );
  654. } else
  655. obj.active = false;
  656. #else
  657. obj.SetActive( active );
  658. #endif
  659. }
  660. private bool _GetActive( GameObject obj )
  661. {
  662. #if UNITY_3_x
  663. return obj.active;
  664. #else
  665. return obj.activeInHierarchy;
  666. #endif
  667. }
  668. private void _SetActiveAndSendMessages( GameObject obj, PoolableObject parentPoolObj )
  669. {
  670. _SetActive( obj, true );
  671. if ( parentPoolObj.sendAwakeStartOnDestroyMessage )
  672. {
  673. obj.BroadcastMessage( "Awake", null, SendMessageOptions.DontRequireReceiver );
  674. if ( _GetActive( obj )
  675. && // Awake could deactivate object
  676. parentPoolObj._wasStartCalledByUnity ) // for preloaded objects Unity will call Start
  677. {
  678. obj.BroadcastMessage( "Start", null, SendMessageOptions.DontRequireReceiver );
  679. }
  680. }
  681. if ( parentPoolObj.sendPoolableActivateDeactivateMessages )
  682. {
  683. obj.BroadcastMessage( "OnPoolableObjectActivated", null, SendMessageOptions.DontRequireReceiver );
  684. }
  685. }
  686. private void _RecursivelySetInactiveAndSendMessages( GameObject obj, PoolableObject parentPoolObj, bool recursiveCall )
  687. {
  688. // Create a local copy of all of the children before we potentially modify it
  689. // by removing a child PoolableObject by making a call to _SetAvailable()
  690. var objTransform = obj.transform;
  691. Transform[ ] children = new Transform[ objTransform.childCount ];
  692. for ( int i = 0; i < objTransform.childCount; i++ )
  693. children[ i ] = objTransform.GetChild( i );
  694. //now recursively do the same for all children
  695. for ( int i = 0; i < children.Length; i++ )
  696. {
  697. Transform child = children[ i ];
  698. var poolableChild = child.gameObject.GetComponent<PoolableObject>();
  699. if ( poolableChild && poolableChild._myPool != null ) //if child is poolable itself it has to be detached and moved to the pool
  700. {
  701. _SetAvailable( poolableChild, true );
  702. }
  703. else
  704. {
  705. _RecursivelySetInactiveAndSendMessages( child.gameObject, parentPoolObj, true );
  706. }
  707. }
  708. if ( parentPoolObj.sendAwakeStartOnDestroyMessage )
  709. {
  710. obj.SendMessage( "OnDestroy", null, SendMessageOptions.DontRequireReceiver );
  711. }
  712. if ( parentPoolObj.sendPoolableActivateDeactivateMessages )
  713. {
  714. obj.SendMessage( "OnPoolableObjectDeactivated", null, SendMessageOptions.DontRequireReceiver );
  715. }
  716. #if UNITY_3_x
  717. #else
  718. if ( !recursiveCall )
  719. #endif
  720. {
  721. _SetActive( obj, false );
  722. }
  723. }
  724. }
  725. static private Dictionary<GameObject, ObjectPool> _pools = new Dictionary<GameObject, ObjectPool>();
  726. static internal ObjectPool _GetPool( PoolableObject prefabPoolComponent )
  727. {
  728. ObjectPool pool;
  729. GameObject prefab = prefabPoolComponent.gameObject;
  730. if ( !_pools.TryGetValue( prefab, out pool ) )
  731. {
  732. pool = new ObjectPool( prefab );
  733. _pools.Add( prefab, pool );
  734. }
  735. return pool;
  736. }
  737. static private void _DetachChildrenAndDestroy( Transform transform )
  738. {
  739. int childCount = transform.childCount;
  740. Transform[ ] children = new Transform[ childCount ];
  741. var myTransform = transform; // cache for performance reasons
  742. int i;
  743. for ( i = 0; i < childCount; i++ )
  744. {
  745. children[ i ] = myTransform.GetChild( i );
  746. }
  747. myTransform.DetachChildren();
  748. for ( i = 0; i < childCount; i++ )
  749. {
  750. GameObject obj = children[ i ].gameObject;
  751. if ( obj )
  752. {
  753. ObjectPoolController.Destroy( obj );
  754. }
  755. }
  756. }
  757. }
  758. /// <summary>
  759. /// Auxiliary class to overcome the problem of references to pooled objects that should become <c>null</c> when
  760. /// objects are moved back to the pool after calling <see cref="ObjectPoolController.Destroy(GameObject)"/>.
  761. /// </summary>
  762. /// <typeparam name="T">A <c>UnityEngine.Component</c></typeparam>
  763. /// <example>
  764. /// Instead of a normal reference to a script component on a poolable object use
  765. /// <code>
  766. /// MyScriptComponent scriptComponent = PoolableObjectController.Instantiate( prefab ).GetComponent&lt;MyScriptComponent&gt;();
  767. /// var myReference = new PoolableReference&lt;MyScriptComponent&gt;( scriptComponent );
  768. /// if( myReference.Get() != null ) // will check if poolable instance still belongs to the original object
  769. /// {
  770. /// myReference.Get().MyComponentFunction();
  771. /// }
  772. /// </code>
  773. /// </example>
  774. public class PoolableReference<T> where T : Component
  775. {
  776. PoolableObject _pooledObj;
  777. int _initialUsageCount;
  778. #if REDUCED_REFLECTION
  779. Component _objComponent;
  780. #else
  781. T _objComponent;
  782. #endif
  783. /// <summary>
  784. /// Initializes a new instance of the <see cref="PoolableReference&lt;T&gt;"/> class with a <c>null</c> reference.
  785. /// </summary>
  786. public PoolableReference()
  787. {
  788. Reset();
  789. }
  790. /// <summary>
  791. /// Initializes a new instance of the <see cref="PoolableReference&lt;T&gt;"/> class with the specified reference.
  792. /// </summary>
  793. /// <param name="componentOfPoolableObject">The referenced component of the poolable object.</param>
  794. #if REDUCED_REFLECTION
  795. public PoolableReference( Component componentOfPoolableObject )
  796. #else
  797. public PoolableReference( T componentOfPoolableObject )
  798. #endif
  799. {
  800. Set( componentOfPoolableObject, false );
  801. }
  802. /// <summary>
  803. /// Initializes a new instance of the <see cref="PoolableReference&lt;T&gt;"/> class from
  804. /// a given <see cref="PoolableReference&lt;T&gt;"/>.
  805. /// </summary>
  806. /// <param name="poolableReference">The poolable reference.</param>
  807. public PoolableReference( PoolableReference<T> poolableReference )
  808. {
  809. _objComponent = poolableReference._objComponent;
  810. _pooledObj = poolableReference._pooledObj;
  811. _initialUsageCount = poolableReference._initialUsageCount;
  812. }
  813. /// <summary>
  814. /// Resets the reference to <c>null</c>.
  815. /// </summary>
  816. public void Reset()
  817. {
  818. _pooledObj = null;
  819. _objComponent = null;
  820. _initialUsageCount = 0;
  821. }
  822. /// <summary>
  823. /// Gets the reference to the script component, or <c>null</c> if the object was
  824. /// already destroyed or moved to the pool.
  825. /// </summary>
  826. /// <returns>
  827. /// The reference to <c>T</c> or null
  828. /// </returns>
  829. public T Get()
  830. {
  831. if ( !_objComponent ) return null;
  832. if ( _pooledObj ) // could be set to a none-poolable object
  833. {
  834. if ( _pooledObj._usageCount != _initialUsageCount || _pooledObj._isAvailableForPooling )
  835. {
  836. _objComponent = null;
  837. _pooledObj = null;
  838. return null;
  839. }
  840. }
  841. return ( T ) _objComponent;
  842. }
  843. #if REDUCED_REFLECTION
  844. public void Set( Component componentOfPoolableObject, bool allowNonePoolable )
  845. #else
  846. public void Set( T componentOfPoolableObject )
  847. {
  848. Set( componentOfPoolableObject, false );
  849. }
  850. /// <summary>
  851. /// Sets the reference to a poolable object with the specified component.
  852. /// </summary>
  853. /// <param name="componentOfPoolableObject">The component of the poolable object.</param>
  854. /// <param name="allowNonePoolable">If set to false an error is output if the object does not have the <see cref="PoolableObject"/> component.</param>
  855. public void Set( T componentOfPoolableObject, bool allowNonePoolable )
  856. #endif
  857. {
  858. if ( !componentOfPoolableObject )
  859. {
  860. Reset();
  861. return;
  862. }
  863. _objComponent = ( T ) componentOfPoolableObject;
  864. _pooledObj = _objComponent.GetComponent<PoolableObject>();
  865. if ( !_pooledObj )
  866. {
  867. if ( allowNonePoolable )
  868. {
  869. _initialUsageCount = 0;
  870. }
  871. else
  872. {
  873. Debug.LogError( "Object for PoolableReference must be poolable" );
  874. return;
  875. }
  876. }
  877. else
  878. {
  879. _initialUsageCount = _pooledObj._usageCount;
  880. }
  881. }
  882. }