Instantiate objects multiple times in one scene play

I am trying to instantiate rice grains when a cup has rotated past a threshold angle, and I also have a reset feature so that if the cup is dropped and collides with the ground then it gets reset to it’s starting position.

However I can’t figure out how to instantiate objects numerous times during a scene play. To clarify, if I rotate the mug enough to invoke the pourRice() method and begin instantiating rice grains, then I drop the mug causing it to be reset to the starting position, if I rotate the mug a second time then the rice won’t instantiate for a second time, just nothing happens… Not sure how to modify my code so that I can continually instantiate rice every time I have rotated the cup past the threshold regardless how many times it has been dropped and reset. Attached is my current classes:

Attached to the cup:
```csharp
**using UnityEngine;

public class Mug : MonoBehaviour
{
public event System.EventHandler OnMugTip;
private float initialAngle, currentAngle;
[HideInInspector] public int count = 0;

void Update()
{
    currentAngle = gameObject.transform.rotation.eulerAngles.y;
    //check if mug has been tipped past a threshold
    if(Mathf.Abs(initialAngle - currentAngle) >= 90)
    {
        if(count == 0)
        {
            Debug.Log("Mug has been tipped past threshold for the first time.");
            this.OnMugTip.Invoke(this, System.EventArgs.Empty);
            count++;
        }
    }
}**

```

using System.Collections;
using UnityEngine;

public class resetPosition_Task4 : MonoBehaviour
{
    public event System.EventHandler MugDropped;

    private Vector3 initialPos;
    private Quaternion initialRot;
    float rotationSpeed = 0.1f;
    public Rice rice;

    [SerializeField] private progressBar progressBar;

    //Get the intial starting position of the gameObject in the level
    void Awake()
    {
        initialPos = transform.position;
        initialRot = transform.rotation;
    }

    //OnCollisionEnter is called when this collider/rigidbody has begun
    //touching another rigidbody/collider.
    void OnCollisionEnter(Collision other)
    {
        if(other.gameObject.tag == "Floor" || other.gameObject.tag == "Wall")
        {
            //Triggers Coroutine and sends 3 second delay
            StartCoroutine(delay(2.5f));
        }
    }

    //Coroutine
    public IEnumerator delay(float delayTime)
    {
        GameObject.Find("ResetText").GetComponent<MeshRenderer>().enabled = true;
        yield return new WaitForSeconds(delayTime);
        GameObject.Find("ResetText").GetComponent<MeshRenderer>().enabled = false;

 
        //Resets the velocity and angular velocity of the gameObject to zero after the collision
        //prior to reseting the gameObject
        GetComponent<Rigidbody>().velocity = Vector3.zero;
        GetComponent<Rigidbody>().angularVelocity = Vector3.zero;

        //Transform back to objects initial position
        transform.position = initialPos;
        transform.rotation = Quaternion.Slerp(transform.rotation, initialRot, Time.time * rotationSpeed);
        rice.resetRice();
    }
}

Attached to the rice prefab that gets instantiated:
```csharp
**using UnityEngine;
using System.Collections;

public class Rice : MonoBehaviour
{[/B]

[B] [SerializeField] private GameObject targetObject;
public Mug mug;

/*
 @brief: subscribes method pourRice() to the OnMugTip event
*/
void OnEnable()
{
    mug.OnMugTip += activateRice;
}


/*
 @brief: Method to trigger the pourRice() method
*/
public void activateRice(object sender, System.EventArgs e)
{
    StartCoroutine(pourRice());
}
/*
 @brief: controls movement of individual physical rice grains - creates 'dumping/pouring' effect
*/
private IEnumerator pourRice()
{
    //instantiate 10 grains of rice
    for(int i = 0; i <= 9; i++)
    {
        Instantiate(Resources.Load("Rice"), targetObject.transform.position, Quaternion.identity);
        this.gameObject.transform.SetParent(null);

        //have some delay between indiv. rice grain instantiation
        yield return StartCoroutine(delay(0.30f));
    }
}

/*
 @brief: Adds a delay between each rice grain or else they 'fall/pour' all in one clump
*/
private IEnumerator delay(float delayTime)
{
    yield return new WaitForSeconds(delayTime);
}

/*
 @brief: method to reset rice to starting position upon mug being dropped
*/
public void resetRice()
{ 
    //reset count so that the we can instantiate
    mug.count = 0;

    GameObject[] physicalGrains = GameObject.FindGameObjectsWithTag("foodTrigger");
    foreach(GameObject element in physicalGrains)
    {
        Destroy(element);
        //element.GetComponent<MeshRenderer>().enabled = false;
    }**

```

You should only instantiate a pool of rice at the start of your application. Then activate them and set position when you wish for them to appear. Deactivate them when you wish for them to disappear.

1 Like

Thanks for the reply!
I like that idea and now I have tried instantiating the objects at the start, setting them to be inactive and adding to a list so that I can easily move through that list later when I want to activate the rice.

However my game freezes in loading when I try to play. Maybe an infinite loop somewhere?

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Rice : MonoBehaviour
{
    [SerializeField] private GameObject targetObject;
    public Mug mug;
    private List<GameObject> physicalGrains = new List<GameObject>();

    private int layer = 0;

    void Awake()
    {
        for(int i  = 0; i <= 10; i++)
        {
            GameObject instance  = (GameObject)Instantiate(gameObject, targetObject.transform.position, Quaternion.identity);
            physicalGrains.Add(instance);
            instance.transform.SetParent(targetObject.transform);
            instance.SetActive(false);
        }
    }

    /*
     @brief: subscribes method pourRice() to the OnMugTip event
    */
    void OnEnable()
    {
        mug.OnMugTip += activateRice;
    }


    /*
     @brief: Method to trigger the pourRice() method
    */
    public void activateRice(object sender, System.EventArgs e)
    {
        StartCoroutine(pourRice());
    }
    /*
     @brief: controls movement of individual physical rice grains - creates 'dumping/pouring' effect
    */
    private IEnumerator pourRice()
    {
        foreach(GameObject grain in physicalGrains)
        {
            grain.SetActive(true);
            grain.transform.SetParent(null);
          
            //have some delay between indiv. rice grain instantiation
            yield return StartCoroutine(delay(0.30f));
        }
    }

 
    /*
     @brief: method to reset rice to starting position upon mug being dropped
    */
    public void resetRice()
    {
        //reset count so that the we can instantiate
        mug.count = 0;

        foreach(GameObject grain in physicalGrains)
        {
            grain.SetActive(false);
            grain.transform.SetParent(targetObject.transform);
        }
  
    private IEnumerator delay(float delayTime)
    {
        yield return new WaitForSeconds(delayTime);
    }

Doing this in Awake is your problem:

GameObject instance  = (GameObject)Instantiate(gameObject, targetObject.transform.position, Quaternion.identity);

Every instance you Instantiate will itself create 10 new instances, ad infinitum. Part of calling Instantiate is calling Awake and OnEnable on the new instance and all its components.

1 Like

Thanks for the reply!
Ah, yes. Thank you for that reminder about the Awake(). I changed the Awake() to Start() and I am able to instantiate 10 rice objects each time I tip the mug which is what I was hoping to do! Thanks.
However now I keep getting this error printed out in the Console:
6101847--663753--upload_2020-7-17_13-15-8.png

So I tried setting the grains active in the OnEnable() function and now they don’t seem to be instantiating at all. Any insight or advice on correcting this would be greatly appreciated

Run a timer in Update to count time instead of IEnumerator.

Update
{
   timer += Time.deltaTime;
   if(timer >= duration)
   {
       //do something
       timer -= duration;
   }
}
1 Like

No sure I totally follow how that will rectify the inactive gameobject error? Are you suggesting replacing my ’ private IEnumerator pourRice()’ method from the code above?
Thanks again for your help.

I am not positive but I think maybe the IEnumerator code does not work because object is not active and that is what throws the error? It would be better to replace that system with what I showed you either way imo.