【问题标题】:How can I prevent my code from yielding every frame?如何防止我的代码产生每一帧?
【发布时间】:2021-09-27 06:58:16
【问题描述】:

我正在调用一个方法,该方法调用自身来爬过地形并创建区域。然而,当区域变大时,进程以堆栈溢出结束。通过强制代码屈服并花时间,它成功完成并爬取了我地图中的 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 而是使用不同的东西,例如Timer
  • @derHugo 你能给我一个例子来说明我是怎么做到的吗?顺便说一句,我在这段代码中所做的是检查 3d 网格中的所有相邻单元格,西东北南上下,递归地,我不知道如果没有协程我该如何调整这个速度
  • 另外,跳过every 100 frames 是什么意思.. 这不是协程的工作方式.. 协程每帧都会向前移动到下一个yield。如果你不yield 它宁可在一帧内执行所有的事情,直到你yield 某处。您的目标是在特定时间过后跳到下一帧吗?
  • 我认为拥有您的实际代码而不仅仅是伪代码会有所帮助;)
  • @derHugo 我想要的是每 100 次递归处理产生一次,因为没有它代码就会崩溃

标签: c# loops unity3d coroutine yield-return


【解决方案1】:

正如一般所说,只要您使用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();
}

然后如前所述,您可以基于时间而不是使用帧/调用计数器而不是使用例如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 之类的值可能已经足够了。

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


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

【讨论】:

  • 我不知道这个使用 movenext() 的手动方法,请给我一些时间试试,听起来很有希望
  • 它不起作用,但后来我用 `` var crcX = crC(); 替换了所有 yield return crC(); while(crcX.MoveNext()) { if(timer >= 100) { yield return null;计时器 = 0; } } ``` 和 crcY crcZ 它像魔术一样工作,它不显示我的调试日志,所以我不知道它为什么工作,但它以某种方式工作
猜你喜欢
  • 2018-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-16
  • 1970-01-01
  • 2011-01-21
  • 1970-01-01
  • 2023-03-19
相关资源
最近更新 更多