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

Question How do I get an object to spawn once randomly at given coordinates and at a given time?

Discussion in 'Scripting' started by JAKunze, Apr 21, 2023.

  1. JAKunze

    JAKunze

    Joined:
    Apr 3, 2020
    Posts:
    4
    I got this prototype that I'm working on. In the third level of the prototype, I want to incorporate some randomness. Essentially, I want an object (in this case, a blue can) to spawn when the timer reaches 20 seconds or lower. I want it to spawn only once, and it needs to be in one of ten different predetermined locations at random. So far, I've got this code:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using TMPro;
    5.  
    6. public class Level3 : MonoBehaviour
    7. {
    8.  
    9.    [SerializeField] GameObject[] blueCans;
    10.    public float time = 120f;
    11.  
    12.    void Update()
    13.    {
    14.       // I have more code above this for the timer, but that's working perfectly.
    15.       if (time <= 20)
    16.       {
    17.          SpawnCan();
    18.       }
    19.    }
    20.  
    21.    void SpawnCan()
    22.    {
    23.       blueCans[Random.Range(0, blueCans.Length)].SetActive(true)
    24.    }
    25.  
    26. }
    Basically, I have 10 cans already in the positions that I want them to possibly spawn. I have them set as inactive in the editor, and I have them all attached to the list in my GameManager object. My goal was to have a random one be set active when the timer reaches 20 seconds or lower. Unfortunately, this code causes them ALL to be active rather than just a random one. What can I do to activate just one?
     
    Last edited: Apr 21, 2023
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,186
    As long as time <= 20, SpawnCan gets called. If you add a few Debug.Log calls into your script, you'll probably see things getting hit multiple times. So, the answer is for your time to be reset either before or after SpawnCan is called. That is of course, the simple solution. The other issue is you could hit the same can over and over, so you might want to remove it from the list or add something to pick a different can if it's already active.
     
  3. JAKunze

    JAKunze

    Joined:
    Apr 3, 2020
    Posts:
    4
    I did try setting time == 20 to make it spawn once exactly at 20 seconds, but that caused it to not do anything at all. I did try that Debug.Log thing, and I was able to see what you meant by it getting called multiple times. So, what can I do? What code can I add? I'm a beginner, so I'm not sure what I can do.
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,612
    You want to make a functioning timer. Before, it would be called every frame forever. Now, it will never be called.

    You actually need to increment a value in order to 'time' something:
    Code (CSharp):
    1. public float Threshold = 2f;
    2.  
    3. private float _currentTime = 0f;
    4.  
    5. private void Update()
    6. {
    7.     _currentTime += Time.deltaTime;
    8.    
    9.     if (_currentTime >= Threshold)
    10.     {
    11.         // do something
    12.     }
    13. }
    You could also reset the timer:
    Code (CSharp):
    1. public float Threshold = 2f;
    2.  
    3. private float _currentTime = 0f;
    4.  
    5. private void Update()
    6. {
    7.     _currentTime += Time.deltaTime;
    8.    
    9.     if (_currentTime >= Threshold)
    10.     {
    11.         // do something
    12.        
    13.         _currentTime -= Threshold;
    14.     }
    15. }
    Hopefully you can understand how you'd use this to do something every n seconds.
     
  5. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    You never increase your time variable.
    This will work.

    Code (CSharp):
    1. public class YourGameManager : MonoBehaviour
    2. {
    3.     private const float spawnTime = 3f;
    4.     private float currentTime;
    5.     private bool objectSpawned;
    6.  
    7.     private void Update()
    8.     {
    9.         currentTime += Time.deltaTime;
    10.         if (currentTime >= spawnTime && !objectSpawned)
    11.         {
    12.             SpawnObject();
    13.         }
    14.     }
    15.  
    16.     private void SpawnObject()
    17.     {
    18.         Debug.Log("Object spawned");
    19.         objectSpawned = true;
    20.     }
    21. }
     
  6. mopthrow

    mopthrow

    Joined:
    May 8, 2020
    Posts:
    348
    The reason it didn't spawn anything at all is because a float timer is imprecise. I can't see your timer, but my guess is you're subtracting Time.deltaTime every Update. Time.deltaTime is the time since the last frame, which at 60 fps is going to be approximately 16.6667 ms. As you can imagine, using your start time of 120 seconds and subtracting 16.6667ms every frame is unlikely to ever land directly on 20 seconds, which is why your == 20 would never fire.
     
  7. JAKunze

    JAKunze

    Joined:
    Apr 3, 2020
    Posts:
    4
    Code (CSharp):
    1. void Update()
    2.     {
    3.         // My timer
    4.         if (time > 0 && !gameManager.gameFailed && !gameManager.gameSuccess)
    5.         {
    6.             if (gameManager.gameIsPaused == false)
    7.             {
    8.                 time -= Time.deltaTime;
    9.                 DisplayTime(time);
    10.             }
    11.         }
    12.         else
    13.         {
    14.             time = 0;
    15.             timerIsRunning = false;
    16.             gameManager.gameFailed = true;
    17.         }
    18.     }
    19. // A method for displaying the time in the UI
    20. void DisplayTime(float timeToDisplay)
    21.     {
    22.         timeToDisplay += 1;
    23.         float minutes = Mathf.FloorToInt(timeToDisplay / 60);
    24.         float seconds = Mathf.FloorToInt(timeToDisplay % 60);
    25.         timerText.text = string.Format("{0:00}:{1:00}", minutes, seconds);
    26.     }
    You would be correct on the timer. Should I instead make time into an int?
     
  8. mopthrow

    mopthrow

    Joined:
    May 8, 2020
    Posts:
    348
    Float is fine, as long as you stop spawning once the timer goes beyond whatever your threshold is, your original solution should work.

    When you call your spawn function for the first time, set a 'hasSpawned' bool to true. Don't run the spawn function again if hasSpawned is true. @MartinMa_ 's solution does this. Alternatively, @spiney199's second piece of code if you want to reset the timer and go around again.

    Just as an added note if you have a timer set up already that is responsible for something else, you don't have to use that timer. It's perfectly fine for the script that needs it to have its own timer to keep things simple instead of trying to use the same timer for everything. I can't be sure you're doing this, but it looks like it from what you posted, so there it is just in case ;)
     
    Last edited: Apr 21, 2023
  9. JAKunze

    JAKunze

    Joined:
    Apr 3, 2020
    Posts:
    4
    @MartinMa_ @spiney199 @mopthrow @Brathnann

    Code (CSharp):
    1. private bool canSpawned;
    2.  
    3. void Update()
    4.     {
    5.         if (time <= 20 && !canSpawned)
    6.         {
    7.             Debug.Log("Spawn");
    8.             SpawnCan();
    9.         }
    10.     }
    11.  
    12. void SpawnCan()
    13.     {
    14.         evilCans[UnityEngine.Random.Range(0, evilCans.Length)].SetActive(true);
    15.         canSpawned = true;
    16.     }
    Thanks everyone for chiming in. This is what I went with, and it's working perfectly!
     
    spiney199 and mopthrow like this.
  10. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455

    No problem, maybe you want add even more randomness so in Start or Awake method you could set spawn time to some random time from range 10-20 for example
     
    Last edited: Apr 21, 2023