c# - Unity Coroutine 等到 true 持续 x 秒
问题描述
我想实现一些与 类似的东西yield return new WaitUntil(() => Check());
,但有一个额外的补充。满足条件后,Check()
如果条件仍然成立,它应该等待 x 秒检查每一帧。
这是我的实现:
private IEnumerator CheckCor(float waitTime)
{
bool checkFlag = true;
bool checkFlag2;
float whileTime;
while (checkFlag)
{
yield return new WaitUntil(() => Check());
checkFlag2 = true;
whileTime = waitTime;
while (whileTime > 0)
{
if (!Check())
{
checkFlag2 = false;
}
whileTime -= Time.deltaTime;
yield return null;
}
if (checkFlag2)
{
checkFlag = false;
}
}
}
Check()
在哪里
private bool Check();
我的实现工作得很好,但似乎有点长。
有没有更短的方法来实现相同的行为?
(也使它通用将是一个加号yield return WaitUntilForSeconds(Check(), 3f);
,其中 Check() 是条件,而 3f 是检查每个帧的条件的时间。我猜它可以使用 来完成CustomYieldInstruction
,但我不确定它是如何工作的。 )
解决方案
将其实现为CustomYieldInstruction
. 将检查器作为 传入Func<bool>
,保留一个标志以记住您是否已启动计时器,并在检查函数在任何时候返回 false 时重置该标志。您甚至可以Func<float>
在计时器重置时接受呼叫并显示剩余时间:
using UnityEngine;
public class WaitUntilForSeconds: CustomYieldInstruction
{
float pauseTime;
float timer;
bool waitingForFirst;
Func<bool> myChecker;
Action<float> onInterrupt;
bool alwaysTrue;
public WaitUntilForSeconds(Func<bool> myChecker, float pauseTime,
Action<float> onInterrupt = null)
{
this.myChecker = myChecker;
this.pauseTime = pauseTime;
this.onInterrupt = onInterrupt;
waitingForFirst = true;
}
public override bool keepWaiting
{
get
{
bool checkThisTurn = myChecker();
if (waitingForFirst)
{
if (checkThisTurn)
{
waitingForFirst = false;
timer = pauseTime;
alwaysTrue = true;
}
}
else
{
timer -= Time.deltaTime;
if (onInterrupt != null && !checkThisTurn && alwaysTrue)
{
onInterrupt(timer);
}
alwaysTrue &= checkThisTurn;
// Alternate version: Interrupt the timer on false,
// and restart the wait
// if (!alwaysTrue || timer <= 0)
if (timer <= 0)
{
if (alwaysTrue)
{
return false;
}
else
{
waitingForFirst = true;
}
}
}
return true;
}
}
}
然后,您可以使用
yield return new WaitUntilForSeconds(Check, 3f);
// or
yield return new WaitUntilForSeconds(Check, 3f, (float t) => {Debug.Log($"Interrupted with {t:F3} seconds left!");});
如果您需要检查器的参数,您可以使用无参数 lambda:
yield return new WaitUntilForSeconds(() => Check(Vector3.up), 3f);
并且,与 lambda 一样,请注意您所做的任何变量捕获。
如果你不想要一个完整的CustomYieldInstruction
,我会用另一个WaitUntil
来暂停:
private IEnumerator CheckCor(float waitTime)
{
bool stillWaiting = true;
while (stillWaiting)
{
yield return new WaitUntil(() => Check());
float pauseTime = waitTime;
yield return new WaitUntil(() =>
{
pauseTime -= Time.deltaTime;
stillWaiting = !Check();
return stillWaiting || pauseTime <= 0;
});
if (stillWaiting)
{
// Stuff when pause is interrupted goes here
}
}
// Stuff after pause goes here
}