Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Object pooling question

Discussion in 'Scripting' started by Phobiegames, Nov 21, 2017.

  1. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
    So I currently have a script where its object pooling some columns that I randomly decided to reuse for coins, which it works perfectly with the coins, until the player collects the coins and the coins are set to inactive, then it stops respawning them even though im telling the script to reactive the current column/coin when it goes to respawn

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Coin : MonoBehaviour {
    6.  
    7.     private void OnTriggerEnter2D(Collider2D collision)
    8.     {
    9.         if (collision.tag == "Bird")
    10.         {
    11.             GameController.instance.Coin();
    12.             gameObject.SetActive(false);
    13.         }
    14.     }
    15. }
    16.  

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class ObjectPooler : MonoBehaviour
    5. {
    6.     public GameObject columnPrefab;                                 //The column game object.
    7.     public int columnPoolSize = 5;                                  //How many columns to keep on standby.
    8.     public float spawnRate = 3f;                                    //How quickly columns spawn.
    9.     public float columnMin = -1f;                                   //Minimum y value of the column position.
    10.     public float columnMax = 3.5f;                                  //Maximum y value of the column position.
    11.  
    12.     private GameObject[] columns;                                   //Collection of pooled columns.
    13.     private int currentColumn = 0;                                  //Index of the current column in the collection.
    14.  
    15.     private Vector2 objectPoolPosition = new Vector2(-15, -25);     //A holding position for our unused columns offscreen.
    16.     public float spawnXPosition = 10f;
    17.  
    18.     private float timeSinceLastSpawned;
    19.  
    20.  
    21.     void Start()
    22.     {
    23.         timeSinceLastSpawned = 0f;
    24.  
    25.         //Initialize the columns collection.
    26.         columns = new GameObject[columnPoolSize];
    27.         //Loop through the collection...
    28.         for (int i = 0; i < columnPoolSize; i++)
    29.         {
    30.             //...and create the individual columns.
    31.             columns[i] = (GameObject)Instantiate(columnPrefab, objectPoolPosition, Quaternion.identity);
    32.         }
    33.     }
    34.  
    35.  
    36.     //This spawns columns as long as the game is not over.
    37.     void Update()
    38.     {
    39.         timeSinceLastSpawned += Time.deltaTime;
    40.  
    41.         if (timeSinceLastSpawned >= spawnRate)
    42.         {
    43.             timeSinceLastSpawned = 0f;
    44.             if(columns[currentColumn].active == false)
    45.             {
    46.                 columns[currentColumn].SetActive(true);
    47.             }
    48.            
    49.             //Set a random y position for the column
    50.             float spawnYPosition = Random.Range(columnMin, columnMax);
    51.  
    52.             //...then set the current column to that position.
    53.             columns[currentColumn].transform.position = new Vector2(spawnXPosition, spawnYPosition);
    54.  
    55.  
    56.             //Increase the value of currentColumn. If the new size is too big, set it back to zero
    57.             currentColumn++;
    58.  
    59.             if (currentColumn >= columnPoolSize)
    60.             {
    61.                 currentColumn = 0;
    62.             }
    63.         }
    64.     }
    65. }
     
  2. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Does that script have any errors?
    I do not think this code is valid?
    Code (csharp):
    1.  
    2. if(columns[currentColumn].active == false)
    3. {
    4.      columns[currentColumn].SetActive(true);
    5. }
    6.  
    If 5 coins is plenty for you, and you're sure you don't need more, I'd suggest that you just set the current column active without checking that's it's active, which would normally be done by activeSelf or activeInHierarchy anyways.
     
  3. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
    Yeah currently no errors, ill give it a try
     
  4. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
    even when I do that, after I collect the 5th coin, it stops re activating them when it does the initial spawn
     
  5. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
  6. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    It's a bit unclear what you mean when you say this.
     
  7. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
    As in when the player touches the coin and it becomes inactive, the coin doesn't respawn active when its coded to. It loops through the 5 pooled coins to reuse them but it never sets them active again.
     
  8. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    First off, I gave kinda bad advice by saying don't check if it's active lol. I don't know what I was thinking, I'm sorry. You don't want to move an active coin. So, here's a simple pool fetch written up. Please try it and see if it works, and /or check the debug log's position of the coin.. because it's gotta be somewhere. :)
    Code (csharp):
    1.  
    2. GameObject GetCoin() {
    3.   for(int i = 0; i < coinList.Count; ++i) {  // coinList : name I created - list holding your coins ;)
    4.      if(!coinList[i].activeSelf) return coinList[i];
    5.     }
    6.    GameObject coin = Instantiate(coinPrefab, positionYouWant);
    7.    return coin;
    8.   }
    9. // elsewhere, where & when you need a coin.
    10. GameObject coin = GetCoin();
    11. coin.position = (whatever);
    12. print("Coin's position is: " + coin.position); // debug, check where it is.. it's gotta be somewhere! :)
    13.  
     
    Phobiegames likes this.
  9. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
    Thank you methos! this bit got my code working properly, I really appreciate it.
     
  10. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
    I only had one more question, how do I get this new version to constantly reuse from the pooled amount?

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class CoinPooler : MonoBehaviour {
    6.  
    7.     public GameObject coinPrefab;
    8.  
    9.     public float spawnRate = 3f;                                    //How quickly columns spawn.
    10.     public float columnMin = -1f;                                   //Minimum y value of the column position.
    11.     public float columnMax = 3.5f;                                  //Maximum y value of the column position.
    12.  
    13.     public float spawnXPosition = 5f;
    14.  
    15.     private float timeSinceLastSpawned;
    16.  
    17.     private Vector2 objectPoolPosition = new Vector2(-15, -25);
    18.  
    19.     private List<GameObject> coinList = new List<GameObject>();
    20.  
    21.     private void Start()
    22.     {
    23.         timeSinceLastSpawned = 0f;
    24.  
    25.         GetCoin();
    26.  
    27.     }
    28.  
    29.     private void Update()
    30.     {
    31.         timeSinceLastSpawned += Time.deltaTime;
    32.         if(timeSinceLastSpawned >= spawnRate)
    33.         {
    34.             timeSinceLastSpawned = 0f;
    35.             // elsewhere, where & when you need a coin.
    36.             float spawnYPosition = Random.Range(columnMin, columnMax);
    37.             GameObject coin = GetCoin();
    38.             coin.transform.position = new Vector2(spawnXPosition, spawnYPosition);
    39.             print("Coin's position is: " + coin.transform.position); // debug, check where it is.. it's gotta be somewhere! :)
    40.         }
    41.     }
    42.  
    43.     GameObject GetCoin()
    44.     {
    45.         for (int i = 0; i < coinList.Count; ++i)
    46.         {  // coinList : name I created - list holding your coins ;)
    47.             if (!coinList[i].activeSelf) return coinList[i];
    48.         }
    49.         GameObject coin = Instantiate(coinPrefab, objectPoolPosition, Quaternion.identity);
    50.         return coin;
    51.     }
    52. }
    53.  
    like, setting a pooled amount to spawn in the beginning and reusing those objects over and over rather than constantly spawning new coins?
     
  11. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Oh, sure, well you could add some (say 5?) to the list in Start(). Then, the function would return 1 if you had less than 5 active. Otherwise, it would create a new one. Oh, I missed something in the code bit.. before you return 'coin', add it to the coinList! lol My bad.

    Also note that your call to "GetCoin()" in start shouldn't really be there, I don't think?

    One thing I didn't do was set the returned coin to 'active'. You can either do that in the method, or do it when you're getting the new coin.:)

    So, code to answer your question + the fix for the method I wrote before:
    Code (csharp):
    1.  
    2. void Start() {
    3.    for(int i = 0; i < 5; ++i) {
    4.       GameObject coin = Instantiate(coinPrefab);
    5.       coinList.Add(coin);
    6.       }
    7.   }
    8. GameObject GetCoin()
    9. {
    10.    for (int i = 0; i < coinList.Count; ++i)
    11.    {  // coinList : name I created - list holding your coins ;)
    12.        if (!coinList[i].activeSelf) return coinList[i];
    13.    }
    14.   GameObject coin = Instantiate(coinPrefab, objectPoolPosition, Quaternion.identity);
    15.    coinList.Add(coin);  // just added this 1 line.
    16.    return coin;
    17. }
    18.  
    It will always re-use from the pooled list, unless you go over, then it creates a new one; that one is also added to the list, now, so you'd have to "need" even more to make it create a new one, again.. Summary: Start with an amount, then only grows when needed ;)
     
  12. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    664
    I usually work with two lists and use a Stack for my pools, something like this.
    Haven't tested it but should work. You can use some error checking but if used properly this wont occur.


    Code (CSharp):
    1. [SerializeField] Transform CoinContainer;
    2. [SerializeField] GameObject CoinPrefab;
    3. const int DEFAULT_COINS = 5;
    4. //Define containers and their estimated maximum size
    5. List<GameObject> ActiveCoins = new List<GameObject>(DEFAULT_COINS);
    6. Stack<GameObject> PooledCoins = new Stack<GameObject>(DEFAULT_COINS);
    7.  
    8. void Start()
    9. {
    10.     for (int i = 0; i < DEFAULT_COINS; ++i)
    11.     {
    12.         DeactivateCoin(GenerateCoin());
    13.     }
    14. }
    15.  
    16. GameObject GetCoin()
    17. {
    18.     GameObject coin;
    19.     if (PooledCoins.Count > 0)
    20.         coin = PooledCoins.Pop();
    21.     else
    22.     {
    23.         coin = GenerateCoin();
    24.     }
    25.     coin.SetActive(true);
    26.     ActiveCoins.Add(coin);
    27.     return coin;
    28. }
    29. void DeactivateCoin(GameObject coin)
    30. {
    31.     coin.SetActive(false);
    32.     ActiveCoins.Remove(coin);
    33.     PooledCoins.Push(coin);
    34. }
    35. GameObject GenerateCoin()
    36. {
    37.     var coin = Instantiate(CoinPrefab, CoinContainer);
    38.     //Do stuff
    39.     return coin;
    40. }