2

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)));
        }
    }

}
user3071284
  • 6,955
  • 6
  • 43
  • 57

1 Answers1

2

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

user3071284
  • 6,955
  • 6
  • 43
  • 57
  • 1
    All true. Just for the record, it is really dangerous (and pointless) to fool around with the execution order, it's a hack. There's absolutely no reason to do that for such a simple problem. Just use a Unity event or ***just call one from the other!*** for such setup – Fattie Feb 04 '16 at 21:31
  • Holy damn i feel stupid now! :) I was apparently looking all the wrong places! I didnt notice i had put the food manager stuff in awake.. Duh! Awake was necessary at one point, but it isnt with my most recent changes.. just forgot to change it back to start! Works perfect now, thanks for the help ! – Ulrik Kroge Sloth Feb 04 '16 at 21:37
  • 1
    I would approve the idea of one calling the other. If you have Cat relying on Owner to be on a specific state, you cannot just assume it will be if you are not 100% sure. So many times, you run in editor and it is all fine, then you run on device and the order is changed and badoom. It is better (in my opinion), to have Owner calling the init on Cat passing itself as parameter (via an interface). Then, you are 110% sure of what is the state of Owner when Cat needs it. – Everts Feb 05 '16 at 08:37
  • @fafase I'd love to hear a little more about your preference... I can't seem to make out why you'd pass Owner to Cat as a parameter. – user3071284 Feb 05 '16 at 16:41
  • Consider the behaviour of your cat to be dependent on the behaviour of the owner, let's say the cat gets vagrant if the owner is careless or a nice cat if the owner is a careful little girl. The cat needs to know that state of the owner. If you set the owner in Awake and the cat reads the state in awake as well, you cannot guarantee the owner to be done first. But from Owner you call cat.Init(this as IOwnerBehaviour); at the end of awake when all is done then your cat is sure to be set according to the state of the owner. Kinda hard to clearly explain in comments tho. – Everts Feb 05 '16 at 20:15
  • You could obviously use the Awake/Start pattern knowing one happens before the other, I just prefer to control from A to Z. – Everts Feb 05 '16 at 20:38