如何让Unity在大量固定时间做某事

时间:2017-08-07 13:41:04

标签: c# unity3d

我有一个时间列表(以秒为单位),需要执行一个功能。我尝试了一种非常天真的方式,但它似乎不起作用,很可能是因为Time.fixedTime并不总是精确的。关于如何执行1000个确定(但不是平均分散)时间的任何更好的想法?

if (Timings.Contains(Time.fixedTime)){
    //do something
}

5 个答案:

答案 0 :(得分:2)

如果你想在特定的时间内继续做某事,你必须在协程中使用Time.deltaTime。使用float0增加Time.deltaTime值,直到达到您想要执行该操作的时间。

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

您可以像下面一样开始尽可能多的任务。该示例运行5秒的代码:

StartCoroutine(executeInWithFixedTiming(5));

您还可以扩展此功能,并使其在该协程中作为Action的参数。然后,您可以传入代码以在该函数内运行。未经测试但也应该有效。

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

然后像这样使用它:

StartCoroutine(executeInWithFixedTiming(
delegate
{
    //DO YOUR STUFF HERE
    transform.Rotate(Vector3.right * Time.deltaTime);
}, 5));

编辑:

  

问题是,我不想在X秒内继续这样做,但是   在每个时间点只有一次

你提到计时器是这样排序的,所以一个for循环遍历它并且while循环以等待计时器完成它应该这样做。

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

只需在Start函数中启动协程,它就应该处理计时器。 StartCoroutine(executeInWithFixedTiming());。您还可以使用此答案中的第二个示例对其进行修改,以使其执行每个代码的参数。

注意

在Unity中,使用Time.deltaTime计算时间会更好。这是我所知道的最准确的计时方式。其他Unity变量往往会随着时间的推移而失去准确性。

答案 1 :(得分:0)

您可以使用Time.realtimeSinceStartup来获取自游戏开始以来的绝对时间,并将其与参考点一起使用以获取自X以来经过的时间。

如果你需要比较很多时间,我建议将它们按顺序进行排序,这样最早就是第一个。这样你只需要检查最早的时间是否已经发生,如果没有,你可以跳过任何更多的检查。

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

如果您的计时发生的次数少于一次,您可以离开而不检查队列中的下一个项目,直到下一帧。

答案 2 :(得分:0)

不要检查确切的时间,而是检查时间是否已过。这将解释fixedTime的不准确性,您的代码将在正确的帧或其后的帧上触发。如果对于您正在做的事情不够及时,请尝试其他答案中概述的一些更准确的计时方法。

将您的计时集合排序并在它们触发时将其删除。然后你可以继续检查第一个(最早的)而不是遍历它们。

if (Time.fixedTime <= Timings[0])

答案 3 :(得分:0)

首先,您需要对列表进行排序,然后完成剩下的工作:

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

答案 4 :(得分:0)

您可以以递归方式使用Invoke。像这样:

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