Reputation: 5407
I have a List of Timings (in seconds), at which a function needs to be executed. I tried a very naive way but it doesn't seem to work, most likely because Time.fixedTime isn't always exact. Any better ideas on how to execute something on 1000 determined (but not equally spread out) times?
if (Timings.Contains(Time.fixedTime)){
//do something
}
Upvotes: 0
Views: 1352
Reputation: 2699
You may be able to use Invoke in a recursive manner. Like this:
void Start() {
Timings.sort();
Timings.reverse(); // use like a stack, removing last elements in order
InvokeRecursive();
}
void InvokeRecursive() {
if (Timings.Count <= 0) return;
float time = Timings[Timings.Count - 1];
Timings.remove(Timings.Count - 1);
// do stuff
Invoke("InvokeRecursive", time - Time.realtimeSinceStartup);
}
Upvotes: 0
Reputation: 125435
If you want to continuously do something for a specific amount of time,you have to use Time.deltaTime
in a coroutine. Increment a float
value from 0
with Time.deltaTime
until it reaches the time you want to do that thing.
IEnumerator executeInWithFixedTiming(float time)
{
float counter = 0;
while (counter <= time)
{
counter += Time.deltaTime;
//DO YOUR STUFF HERE
transform.Rotate(Vector3.right * Time.deltaTime);
//Wait for a frame so that we don't freeze Unity
yield return null;
}
}
You can start as many tasks as possible like below. The example run the code for 5
seconds:
StartCoroutine(executeInWithFixedTiming(5));
You can also extend this function and make it take a parameter of what to do in that coroutine as Action
. You can then pass in the code to run inside that function too. Not tested but should also work.
IEnumerator executeInWithFixedTiming(Action whatToDo, float time)
{
float counter = 0;
while (counter <= time)
{
counter += Time.deltaTime;
whatToDo();
//Wait for a frame so that we don't freeze Unity
yield return null;
}
}
then use it like this:
StartCoroutine(executeInWithFixedTiming(
delegate
{
//DO YOUR STUFF HERE
transform.Rotate(Vector3.right * Time.deltaTime);
}, 5));
EDIT:
The thing is, I don't want to continuously do it for X seconds, but only once at each point of Timings
You mentioned that the timer is sorted so a for
loop to loop through it and while
loop to wait for the timer to finish should do it.
List<float> timer = new List<float>();
IEnumerator executeInWithFixedTiming()
{
float counter = 0;
//Loop through the timers
for (int i = 0; i < timer.Count; i++)
{
//Wait until each timer passes
while (counter <= timer[i])
{
counter += Time.deltaTime;
//Wait for a frame so that we don't freeze Unity
yield return null;
}
//TIMER has matched the current timer loop.
//Do something below
Debug.Log("TIMER REACHED! The current timer is " + timer[i] + " in index: " + i);
}
//You can now clear timer if you want
timer.Clear();
}
Just start the coroutine once in the Start
function and it should handle the timer. StartCoroutine(executeInWithFixedTiming());
. You also also modify it with the second example in this answer to make it take a parameter of each code to execute.
Note:
In Unity, it's better to time something with Time.deltaTime
. It's the most accurate way of timing that I know about. Other Unity variables tend to loose their accuracy over time.
Upvotes: 2
Reputation: 228
At first you need sort your list, then do the rest:
float time = 0;
Int count = 0;
Lisr<float> timeList;
void Update()
{
time += Time.deltaTime;
If(timeList.count > count)
{
If(time >= timeList[count])
{
//do something
++count;
}
}
}
Upvotes: 0
Reputation: 66
Instead of checking against the exact time, check whether that time has passed. This will account for the inaccuracy of fixedTime and your code will fire on the correct frame or the one after it. If that's not timely enough for what you're doing, try some of the more accurate timing methods outlined in the other answers.
Keep your collection of timings sorted and remove them as they fire. Then you can keep checking the first (earliest) instead of iterating through them all.
if (Time.fixedTime <= Timings[0])
Upvotes: 0
Reputation: 4960
You can use Time.realtimeSinceStartup to get the absolute time since the game started and use that with a reference point to get the elapsed time since X.
If you need to compare a lot of timings against that I suggest sorting them into order so the earliest is first. That way you only ever need to check if the earliest timing has happened, if it hasn't you can skip any more checks.
float refTime;
Queue<float> timings;
void Update() {
var elapsed = Time.realtimeSinceStartup - refTime;
//test against the earliest timing
if (timings.Count > 0 && refTime > timings.Peek()) {
//do stuff
//remove earliest timing from queue
timings.Dequeue();
}
}
If your timings happen less often than once a frame you can get away without checking the next item in the queue until next frame.
Upvotes: 0