如何使用协程来检测玩家是不是静止了足够长的时间
Posted
技术标签:
【中文标题】如何使用协程来检测玩家是不是静止了足够长的时间【英文标题】:How to use coroutines to detect if the player is still for long enough如何使用协程来检测玩家是否静止了足够长的时间 【发布时间】:2021-08-04 07:03:30 【问题描述】:我不太擅长协程,我正在尝试在我的游戏中做点什么。我的目标是做到这一点,如果玩家保持静止一段时间(在本例中为 3),它将调用一个函数。我正在尝试使用协程,但我无法让它们工作。到目前为止,这是我的代码:
...
public float waitTime;
void Update()
if (Input.GetKey(KeyCode.E) && rb.velocity.x <= stillVelocity.x && rb.velocity.y <= stillVelocity.y)
StartCoroutine(coroutine());
else
StopCoroutine(coroutine());
IEnumerator coroutine()
yield return new WaitForSeconds(waitTime);
method();
void method()
Debug.Log("It all worked");
我确实设法在我的日志中获得了“一切正常”,但不是我想要的方式。即使我在 waitTime 过去之前放开 E,它也会打印出来。速度检查工作正常。
现在,我可能应该说出我想让脚本做什么。我希望脚本检测玩家是否静止并且他们是否按住 E 键 waitTime 秒。这将如何应用于实际游戏是这样的:我正在制作一个玩家可以设置检查点的系统。从那时起,此检查点就是生成点,直到创建新的检查点。我可以自己做检查点/生成点的事情,只需要协程方面的帮助。
我可能对使用协程的想法有误。我选择在这种情况下使用它们,因为我听说当我的代码与时间一起工作时我应该使用它们。我还认为我需要更多地练习使用它们才能更好地理解它们。
这是我的问题:
我在这里使用协程是否正确? 如果是这样,我将如何使用协程? 如果没有,我应该什么时候使用它们?(请在您的回答中使用协程,除非没有它们绝对不可能。我更喜欢这个,因为我可以在没有协程的情况下做到这一点,但是,如上所述,我需要实践。)
【问题讨论】:
【参考方案1】:Coroutine
可以在这里工作,但由于您只是想在一段时间后调用一个函数,Involke
可能会更好。当我只需要在 X 秒后调用一个函数时,我倾向于使用 Involke
,但是当我需要创建一个基于一系列先决条件以特定方式或特定时间量运行的函数时,我会使用 Coroutine
.
我目前在您的代码中看到的一个问题是您不断调用StartCoroutine
而不检查Coroutine
是否处于活动状态。每当我需要在实际Coroutine
的生命周期内多次出现条件的情况下使用Coroutine
时,我将存储一个引用并检查该引用是否为空。
...
public float waitTime;
private Coroutine YourCoroutineReference = null;
void Update()
if (Input.GetKey(KeyCode.E) && rb.velocity.x <= stillVelocity.x && rb.velocity.y <= stillVelocity.y)
if(YourCoroutineReference == null)
YourCoroutineReference = StartCoroutine(coroutine());
else
if(YourCoroutineReference != null)
StopCoroutine(YourCoroutineReference);
IEnumerator coroutine()
yield return new WaitForSeconds(waitTime);
method();
YourCoroutineReference = null;
void method()
Debug.Log("It all worked");
使用此附加条件检查,打印输出应按预期工作。但是,我会在此处使用调用,因为您的用例会在固定时间后触发单一方法。
这里是使用Invoke
的例子
...
public float waitTime;
void Update()
if (!IsInvoking("method") && Input.GetKey(KeyCode.E) && rb.velocity.x <= stillVelocity.x && rb.velocity.y <= stillVelocity.y)
Invoke("method", waitTime);
else
if(IsInvoking("method"))
CancelInvoke("method");
private void method()
Debug.Log("It all worked");
两个 sn-ps 都未经测试,但大体方向在那里。如果您遇到问题,请告诉我,我可以更新 sn-ps。同样,如果您发现自己只是在一段时间后使用Coroutine
调用函数,请使用Invoke
。当您在连续布置多个函数调用或 Lerps 的情况下执行更复杂的操作时,Coroutine
是一个非常有用的工具。
我还要补充一点,Coroutines
非常灵活,您可以使用yields
出于任何原因暂停它们,然后继续它们,而Invoke
通常在 X 秒后调用一次,或者在每 Y 秒后调用一次如果您使用InvokeRepeating
,则为 X 秒。使用Coroutines
的另一个有用部分是您可以将参数传递给它们,而使用Invoke
,您不能。我想一种过于简单的思考方式是Invoke
是一个非常简单的一般情况,适用于一次性使用Coroutines
或极其固定的时间重复方法。
【讨论】:
谢谢。我之前听说过Invoke
,但不知道其余的调用变量,如 IsInvoking。谢谢你的帮助。我可以在下午的某个时间对此进行测试,然后我可以将您的回答标记为已接受。【参考方案2】:
您可以将协程修改为不等待,不断检查玩家是否静止并在 3 秒后调用,否则中断。
IEnumerator coroutine()
float limit = 3;
float elapsed = 0;
while(elapsed < limit)
if(!IsPlayerStill())
yield break;
elapsed += Time.deltaTime;
yield return null;
method();
【讨论】:
【参考方案3】:如果你有一个Update
无论如何都要检查一下,我不会在这里使用协程,而是使用一个简单的计数器,比如
float timer;
bool alreadyFired;
void Update()
if (Input.GetKey(KeyCode.E) && rb.velocity.x <= stillVelocity.x && rb.velocity.y <= stillVelocity.y)
timer -= Time.deltaTime;
if(!alreadyFired && timer <= 0)
alreadyFired = true;
method();
else
alreadyFired = false;
timer = waitTime;
在这个用例中,我认为这比协程更容易维护。
如果你真的想使用协程,我会这样做
private Coroutine _routine;
void Update()
if (Input.GetKey(KeyCode.E) && rb.velocity.x <= stillVelocity.x && rb.velocity.y <= stillVelocity.y)
if(_routine == null) _routine = StartCoroutine(DoAfterSeconds(waitTime, method));
else
if(_routine != null) StopCoroutine(_routine);
_routine = null;
private IEnumerator DoAfterSeconds (float duration, Action callback)
yield return new WaitForSeconds (duration);
_routine = null;
callback?.Invoke();
【讨论】:
以上是关于如何使用协程来检测玩家是不是静止了足够长的时间的主要内容,如果未能解决你的问题,请参考以下文章