如何防止我的代码产生每一帧?

How can I prevent my code from yielding every frame?

我正在调用一种方法,该方法会调用自身来爬过地形并创建区域。然而,当区域变大时,该过程以堆栈溢出结束。通过强制代码让步并花时间它成功完成并爬行我地图中的 3 个区域。然而,我使用的方法是产生每一帧,我不知道如何让它每 100 帧产生一次,导致它完成速度非常慢。这是我为提高可读性所做的伪代码:


 
public int timer = 0;
 
void Awake(){
 
StartCoroutine(crA);
}
 
public IEnumerator crA(){
//stuff
 
yield return StartCoroutine(crB());
 
//stuff that needs to happen only after crB finishes
}
 
public IEnumerator crB(){
 
timer = 0;
 
yield return StartCoroutine(crC());
 
}
 
public IEnumerator crC(){
//Crawiling code, crawls terrain to create a zone
 
if(x){ yield break;}
 
timer++;
 
//vv I WANTED IT TO YIELD ONLY IN HERE
if (timer ==100){
timer = 0;
yield return null;
}
//^^
 
yield return StartCoroutine(crC());
yield return StartCoroutine(crC());
yield return StartCoroutine(crC());
yield return StartCoroutine(crC());
yield return StartCoroutine(crC());
 
}
 

似乎 yield return startcoroutine 导致了 yield,但我不知道该用什么代替。任何帮助将不胜感激。

一般来说只要你使用yield它至少会yield一帧像

yield return null;

会。

您可以做的是“手动”使用 IEnumerator.MoveNext 而不放弃它。如果您使用 StartCoroutine.

,这基本上就是 Unity 协程每帧调用一次的内容

然后只有在你想这样做的时候才屈服。给定您的伪代码,例如

private void Awake()
{
    StartCoroutine(crA);
}

public IEnumerator crA()
{
    //stuff

    yield return crB();

    //stuff that needs to happen only after crB finishes
}

public IEnumerator crB()
{ 
    timer = 0;

    // "Manually" run the routine instead of using Unity Coroutine interface
    var crc = crC();
    while(crc.MoveNext())
    {
        if(timer >= 100)
        {
            yield return null;
            timer = 0;
        }  
    } 
}

public IEnumerator crC()
{
    //Crawiling code, crawls terrain to create a zone

    if(x) yield break;

    timer++;

    yield return crC();
    yield return crC();
    yield return crC();
    yield return crC();
    yield return crC();
}

然后如前所述,您可以基于时间而不是使用 frame/call 计数器而不是使用例如一个 StopWatch

private const float TARGET_FRAMERATE = 60;

public IEnumerator crB()
{ 
    var targetMilliseconds = 1000f / TARGET_FRAMERATE;
    var sw = new StopWatch();
    sw.Start();

    // "Manually" run the routine instead of using Unity Coroutine interface
    var crc = crC();
    while(crc.MoveNext())
    {
        if(sw.ElapsedMilliseconds >= targetMilliseconds)
        {
            yield return null;
            sw.Restart();
        }  
    } 
}

所以只要最后一次执行超过目标帧率,它就会产生一帧。您必须对值进行一些调整。根据您的用例,30 甚至 24 可能已经足够了。

它基本上是帧速率和实际实时持续时间之间的权衡。


注意:在智能手机上打字,但我希望思路清晰