有关通过协程暂停if语句的问题 第0步:定义等待时间第1步:累加器变量步骤2:每帧累积Time.deltaTime 第3步:检查是否该生成新狗了!注释

问题描述

我刚刚开始学习如何在Unity中编程。在过去的两三个星期里,每天都在尝试编写一些代码。我在一个简单的脚本上有些卡住了。该脚本如下:

`using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerControllerX : MonoBehavIoUr
{
    public GameObject dogPrefab;
    public float timeWaited = 0.5f;


    public void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        // On spacebar press,send dog
        if (Input.GetKeyDown(KeyCode.Space))
        { 
            StartCoroutine(MyCoroutine());
        }
        

    }
    private IEnumerator MyCoroutine()
    {
        Instantiate(dogPrefab,transform.position,dogPrefab.transform.rotation);
        yield return new WaitForSeconds(1f);
        Debug.Log("Waited 0.5 seconds!");
        
       
    }
} `

我希望在空格键按压之间有0.5秒的间隔(这会产生一条狗)。我认为这是行不通的,因为协程仅在按空格键时才开始播放。但是,我想不出办法解决此问题。任何建议将不胜感激!

解决方法

每按一次空格键,您的代码就会启动一个新的协程。即使您在每个协程实例中在技术上等待了0.5秒,也不会阻止启动新的协程。因此,您几乎是正确的-协程从顶部重新启动-它的一个新实例-不一样。此外,语句if (Time.time >= timeWaited)始终在前几帧之后为true-它返回游戏开始以来的总秒数。https://docs.unity3d.com/ScriptReference/Time-time.html

相反,让我们使用标准方法解决此问题-一个累加器值,该值每帧增加Time.deltaTime。 (https://docs.unity3d.com/ScriptReference/Time-deltaTime.html

第0步:定义等待时间

您的代码中已经包含此名称-称为timeWaited。为了清楚起见,我将在此处重命名。此值应该是脚本的全局值(用Unity术语来说)-这是您的 MonoBehaviour 派生类中的 class字段

float minimumTimeBetweenDogSpawns = 0.5f;

第1步:累加器变量

您的代码中没有此变量,因此我在这里介绍它。此值应该是脚本的全局值(用Unity术语来说)-这是您的 MonoBehaviour 派生类中的 class字段

float elapsedTimeSinceSpacebarLastPressed = 0;  // feel free to make the name shorter,lol!

步骤2:每帧累积Time.deltaTime

现在,Unity游戏引擎(实际上是所有游戏引擎)会跟踪每个帧之间经过的时间。这是游戏开发中非常重要的概念,因为它可以使对象平稳移动,而不管帧花费更长或更短的时间运行-因为我们知道自上一帧以来的经过时间,因此我们可以将其用于计算对象位置,单位应前进多少,等等。在我们的情况下,我们只是使用此值到达所需的点-在您的情况下为0.5秒,由minimumTimeBetweenDogSpawns变量表示。

那么,我们如何以及在哪里累积经过的时间?简单-只需将其添加到Update

void Update()
{
   elapsedTimeSinceSpacebarLastPressed += Time.deltaTime;
}

第3步:检查是否该生成新狗了!

那么,我们如何处理这个elapsedTimeSinceSpacebarLastPressed?到目前为止,它一直在增长……我们需要检查它是否达到了兴趣的价值-在我们的例子中,已经达到minimumTimeBetweenDogSpawns。但仅在按下空格键的情况下。让我们在if中添加一个Update语句来做到这一点。我将从步骤2展开Update

void Update()
{
   elapsedTimeSinceSpacebarLastPressed += Time.deltaTime;

   if(Input.GetKeyDown(KeyCode.Space) 
      && elapsedTimeSinceSpacebarLastPressed >= minimumTimeBetweenDogSpawns)
   {
      // Do stuff! Spawn a dog! Run a coroutine! Have a ball!
    
      elapsedTimeSinceSpacebarLastPressed = 0;   // ...but don't forget to reset the accumulator!
   }
}

您抓住了最后一行吗?非常重要-重置累加器!。这将重新启动该过程,使我们可以生成更多的狗,但不能达到minimumTimeBetweenDogSpawns指定的值。

注释

  • 从技术上讲,协程不是必需的,但它很有用-假设您想执行一些耗时的任务-例如,狗的动画制作时间约为10分钟。半秒,这是在生成之间延迟的很好原因。

  • 与原始帖子中的一些评论相反,机器之间在速度方面的差异无关紧要 !时间就是时间-机器之间可能发生的所有变化是在下一次生成之前运行了多少帧-但是时间没有过去!在编写正确的游戏中,我们从不关心过去有多少帧-我们依赖计时,这就是每个游戏引擎和框架提供deltaTime某个版本的原因。 >

,

首先,我不相信Time.time会那样工作。 Unity documentation读为:

此帧开始的时间(只读)。这是自游戏开始以来的时间(秒)。

Time.time是应用程序已运行的时间(以秒为单位)。它是只读的。

协程中的if语句正在检查Time.time,因此它将始终大于0.5,并且将始终生成狗。

尽管技术含量较低,但是要解决此问题,我会在协程的开始和结束处设置一个变量,这样您就可以针对该变量检查另一个if语句。这也意味着您可以将此检查放在Input.GetKeyDown()检查旁边,而不必一次运行多个协程。

这意味着您的脚本主体看起来像这样:

public GameObject dogPrefab;
bool Waiting = false;

void Update() 
{
     if (Input.GetKeyDown(KeyCode.Space) && !Waiting)
     {
         StartCoroutine(MyCoroutine());
     } else
     {
         Debug.Log("Waiting");
     }
}

IEnumerator MyCoroutine()
{
    Waiting = true;

    Instantiate(dogPrefab,transform.position,dogPrefab.transform.rotation);
    yield return new WaitForSeconds(0.5f);
    Debug.Log("Waited 0.5 seconds!");

    Waiting = false;
}

(CoolBots已经在评论中说了大部分,但是我无法评论,所以我还是想把它放进去。我也有使用很多协程的坏习惯,因此他们说可能没有必要,但是我个人会这样。)

,

您不需要协程执行此操作。您可以简单地使用timeWaited变量,每按一次按钮,就会生成一条狗,并将变量与游戏开始以来的经过时间和等待时间相加。

类似这样的东西:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerControllerX : MonoBehaviour
{
    public GameObject dogPrefab;
    public float timeWaited = 0.5f;


    public void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        // On spacebar press,send dog
        if (Input.GetKeyDown(KeyCode.Space) && Time.time >= timeWaited)
        { 
            SpawnDog();
            timeWaited = Time.time + timeWaited;
        }
        

    }
    private void SpawnDog()
    {
       
         Instantiate(dogPrefab,dogPrefab.transform.rotation);
        Debug.Log("Waited 0.5 seconds!");
       
    }
} `