问题描述
我刚刚开始学习如何在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!");
}
} `