Search code examples
c#listunity-game-enginepooling

Unity3D: List returns null when accessed from other class


I am currently attempting to make a small mobile game in unity3D, and as i always hear that using instantiate/destroy is a very bad way to go (though i have never made anything large enough where this actually became an issue) i decided to try and make at pooling system.

I have create a script that handles all the pooled objects in two separate lists. I can successfully spawn an object from the pool by calling the method from the script itself, but the issue is that when i try to call the method from another script, it does not work. With the different attempts i have made, i have gotten either null reference exceptions, or gotten the error:

Operation is not valid due to the current state of the object System.Linq.Enumerable.First[GameObject] (IEnumerable`1 source)

I cannot really find anyone else having the problem. And the solutions that seem to be related does not work. The primary issue might also be.. Why does this happen?

Pool Script:

public class ObjectPool : MonoBehaviour {


    GameObject basicCell;
    GameObject basicFood;

    private Vector3 poolSpawnPos;

    [SerializeField]
    private int cellAmount;

    [SerializeField]
    private int foodAmount;

    List<GameObject> cellPool;
    List<GameObject> foodPool;

    GameObject foodManager;
    GameObject cellManager;
    // Use this for initialization
    void Awake () {

        foodManager = gameObject.transform.FindChild ("FoodPool").gameObject;
        cellManager = gameObject.transform.FindChild ("CellPool").gameObject;

        cellPool = new List<GameObject> (cellAmount);
        foodPool = new List<GameObject> (foodAmount);
        poolSpawnPos = new Vector3 (8000, 8000, 8000);
        basicFood = Resources.Load ("Food/BasicFood/BasicFood") as GameObject;
        basicCell = Resources.Load ("Cell/Cell") as GameObject;
        for (int i = 0; i < cellAmount; i++) {
            GameObject currentCell = Instantiate(basicCell, poolSpawnPos, Quaternion.identity) as GameObject;
            currentCell.transform.parent = cellManager.transform;
            currentCell.SetActive (false);
            cellPool.Add (currentCell);

        }

        for (int i = 0; i < foodAmount; i++){
            GameObject currentFood = Instantiate(basicFood, poolSpawnPos, Quaternion.identity) as GameObject;
            currentFood.transform.parent = foodManager.transform;
            currentFood.SetActive (false);
            foodPool.Add (currentFood);
        }
    }

    // Update is called once per frame
    void Update () {
        //if(foodPool.Count > 0)
        //  SpawnFood (new Vector3 (0, 0, 0));
    }

    public void SpawnFood(Vector3 spawnPosition)
    {
        GameObject selectedFood = null;
        selectedFood = foodPool.First();
        print (selectedFood);
        selectedFood.SetActive (true);
        foodPool.Remove (selectedFood);

        selectedFood.transform.parent = null;
        selectedFood.transform.position = spawnPosition;


        //set food Stats


    }


    public void KillFood(GameObject killedFood)
    {
        foodPool.Add (killedFood);
        killedFood.transform.position = poolSpawnPos;
        killedFood.transform.parent = foodManager.transform;
        killedFood.SetActive (false);

    }
}

Script from which i need to call the method:

public class FoodManager : MonoBehaviour {

    public ObjectPool pool;
    public int initialFoodNumber = 50;

    void Awake()
    {
        pool = GameObject.Find("ObjectPool").GetComponent<ObjectPool>();

        for (int i = 0; i <= initialFoodNumber; i++)
        {
            pool.SpawnFood(new Vector3(Random.Range(-40, 41), 0, Random.Range(-40, 41)));
        }
    }

}

Solution

  • You can change the Awake() in FoodManager to Start() in order to ensure that the code in ObjectPool gets called first.

    Or you can explicitly set the script execution order for these 2 classes. This is done by going to the Edit > Project Settings > Script Execution Order menu and adding both scripts, ObjectPool being ranked above FoodManager. Source