How to instantiate prefabs at certain times?
Hello guys!
I'm trying to make a mini Guitar Hero clone for a project in school. I am new to Unity and lack many knowledge and coding skills. Right now I am struggling to instantiate prefabs (which would be the "notes" or "frets" of the game) at certain seconds.
For example, I have a 30 second song and would like to instantiate prefabs at the seconds 3, 5, 9, etc.
I came up with a very basic and primitive code by making a timer and instantiating from an array of prefabs when the timer is a certain value, like this:
float timer;
void Update () {
timer += Time.deltaTime;
if (timer == 5)
Instantiate (Notes [blue], BlueSpawn.position, Quaternion.identity);
if (timer == 10)
Instantiate (Notes [red], RedSpawn.position, Quaternion.identity);
But well, I would end up making a wall of "if" and the code doesn't instantiate anything at all. Is there a better way of generating prefabs at certain points in time? Thanks!
Answer by wibble82 · Dec 07, 2015 at 10:12 AM
Hi there
The first problem you're hitting is that 'timer' is a floating point number - i.e. it can contain fractional values. As a result, even if the maths looks like timer should eventually equal 5 or 10, its unlikely to ever be exactly 5 or 10. So if Time.deltaTime is 1/60, you might expect that after 60 frames time is 1, but it might actually be 0.99999999 or 1.00000001. For this reason, precise comparisons of floating point numbers are rarely useful. Normally to see if a floating point value is equal to something, your code would look more like:
//if timer is 'almost exactly'' 5 - i.e. more than 4.99999 or less than 5.00001
if(timer >= 4.99999 || timer <= 5.00001)
Though a better way to write it might be:
//a cleaner way of writing it - if the absolute (i.e. positive) value given
//by timer-5 is less than 0.00001
if(Mathf.Abs(timer-5.0f) < 0.000001f)
However, for your purposes a simple comparison is probably not what you want, as Time.deltaTime varies and is big enough that you might skip over 5 (or any of the other numbers) altogether! For example, timer might be 4.9 one frame, then 5.1 the next - skipping 5 altogether!
A more common way of writing your logic would be to record a bit of extra information - either the next time we're going to spawn, or the previous time we spawned. In addition, we might use the Time.time value rather than Time.deltaTime, to avoid inacuracies in our time tracking:
float next_spawn_time;
void Start()
{
//start off with next spawn time being 'in 5 seconds'
next_spawn_time = Time.time+5.0f;
}
void Update()
{
if(Time.time > next_spawn_time)
{
//do stuff here (like instantiate)
Instantiate(bla bla bla)
//increment next_spawn_time
next_spawn_time += 5.0f;
}
}
This approach has the advantages:
It never 'misses' the time to spawn, as its only ever checking if 5s have passed since the last spawn
It works no matter how much time has passed
It isn't dependent on comparing numbers to each other
It doesn't get increasingly innacurate, as its using the accurate Time.time value
It's only disadvantage is that you are reliant on Time.time, so if you wanted your timing to be independent (say for example you only wanted to 'increment' time when the player was holding down a button, it wouldn't work. In this case you'd write very similar code, however you'd keep track of the current 'timer' yourself - just like in your first example.
Hope that helps and things are a bit clearer now.
Tip: If you want to start dealing with much smaller time steps (ones that might be closer to Time.deltaTime itself), you'd need to get a bit cleverer and use a while loop, potentially spawning multiple objects per frame. I'll leave that to you though if you do eventually need it :)
-Chris
Thank you! This worked for me. I also used an array of float numbers to deter$$anonymous$$e the seconds in which they will appear, combined with your code they totally work now! Thank you Chris!