我已经阅读了this和官方文档:fixedUpdate()和deep explanation。
所以我试图分离我的代码。首先,在Update()
中,我没有给出完整的代码,这些变量是不言自明的:
private void Update()
{
if (Input.GetButton("Jump")) {
if (groundsTouched>0) {
_jumpRequest = true;
} else {
_keepOnJumping = true;
}
} else {
_keepOnJumping = false;
}
/* Handle release button: */
_fallRequest = true;
}
现在我像这样在FixedUpdate()
中进行所有计算:
private void FixedUpdate()
{
if (_jumpRequest) {
if (!_jumpGravitySent) {
_jumpGravitySent = true;
_animator.SetBool("Jump", true);
_jumpRequest = false;
jumpTimeCounter = jumpTime;
/* Cancel all force (couldn't find a better way) */
_rigidbody.velocity = Vector3.zero;
_rigidbody.angularVelocity = Vector3.zero;
_rigidbody.AddForce(
Vector3.up * jumpVelocity, ForceMode.VelocityChange
);
}
} else if (_keepOnJumping) {
jumpTimeCounter -= Time.fixedDeltaTime;
if (jumpTimeCounter >= 0) {
_rigidbody.AddForce(
Vector3.up * jumpVelocity * jumpKeepMultiplier,
ForceMode.Acceleration
);
}
}
if (groundsTouched == 0 &&
_rigidbody.velocity.y > velocityFallMin &&
_rigidbody.velocity.y < velocityFallMax
) {
_animator.SetBool("Jump", false);
_animator.SetBool("Fall", true);
}
if (_fallRequest) {
_fallRequest = false;
_jumpGravitySent = false;
_keepOnJumping = false;
}
}
我遇到的问题真的很奇怪:当FPS较低时,玩家无法跳高。
Unity质量检查人员发现了我的问题,他们的回答是:
您要添加的力取决于fixedDeltaTime,后者取决于 您的可用性能(或本质上是帧速率)。
如果转到“编辑”->“项目设置”->“时间”并将固定的“时间步长”更改为 较大的值,您将获得预期的行为。尝试一些 固定时间步长的不同值,并查看行为如何变化。
另一个建议是重写代码,使其不依赖 帧速率(例如,使用“力度”而不是强制或添加特定量) 的力量,而不取决于时间步长。
“另一个建议是重写代码,使其不依赖于帧频”->您将如何做,我以为上面的代码正在这样做!
我想念什么?我在做什么错/解决方案是什么?
答案 0 :(得分:1)
我想知道是谁给了您这个答案的Unity QA,这很令人毛骨悚然。
让我解释一下实际发生的情况 。
1)让我们从fixedDeltaTime
开始:此值从不取决于帧速率。可以在编辑器中(在Edit->Project Settings->Time
中进行设置,这是在运行时保留的值,除非任何脚本都会通过赋值对其进行更改。 Unity引擎永远不会对其进行更改。
2)物理循环:在完整的引擎循环中,Unity将执行多个物理循环(0,1或更多),然后执行一个渲染循环。每个Rendering循环执行的Physics循环数基于fixedDeltaTime
以及自上一个循环以来经过的时间(即:Rendering循环的deltaTime
)。
例如,假设fixedDeltaTime = 0.0166667
,并且自上次物理循环以来经过的时间小于10
ms。 Unity将不会执行物理循环。现在假设甚至下一帧都以10
ms渲染-这意味着自从上一次物理循环以来,20
ms已经过去。由于此值比fixedDeltaTime
高,因此Unity将执行物理循环。
有时可能会发生帧渲染速度非常慢的情况(由于不可预见的原因),例如40
ms。为了保持物理模拟的一致性,Unity必须连续运行 two 物理循环,这是因为0.04/0.0166667 = 2.4
。
请记住,Unity会跟踪上一个Physics循环的开始时间与下一个Physics循环的开始时间之间的差异:如果渲染每帧持续10
ms毫秒,并且fixedDeltaTime
设置为{{1 }} ms(60Hz),一旦启动运行时,Unity将执行第一个Physics循环,然后在第一个渲染帧后跳过一个(因为仅经过166667
ms而不是10
) ,然后在第二个渲染帧(针对166667
传递20
ms之后执行一个“物理”循环。但是现在我们有了166667
ms不同步的循环,因此Unity会对此进行跟踪。
在第3帧之后,又经过了3.3333
毫秒,但是由于10
仍然低于10+3.3333 = 13.3333
,因此不会执行任何物理循环。现在,让我们假设第4个渲染帧“出错”,并且持续fixedDeltaTime
毫秒而不是25
。在下一个Physics循环开始时,自上次Physics更新以来总共经过了10
和25+13.3333 = 38.3333
,Unity将连续执行 two Physics循环以在渲染第5帧之前,请紧跟固定步长仿真。
所有介绍之后,让我们回到您的问题,看看会发生什么:
在特定时刻执行38.3333/16.6667 = 2.3
,并设置Update()
和_jumpRequest = true;
。
在此渲染帧之后,第一次执行_fallRequest = true;
,执行FixedUpdate()
行,并设置AddForce ForceMode.VelocityChange
,_fallRequest = false;
和_jumpGravitySent = false;
。 _keepOnJumping = false;
结束后,Unity将执行物理模拟,并借助物理引擎来调整刚体的位置和速度。
现在问题触发了:由于渲染帧很慢,因此物理循环至少连续执行两次,但是在两者之间没有执行FixedUpdate()
,因此Update()
中的所有内容都会被跳过,但是物理模拟是第二次运行,将刚体位置相对于预期的最高位置向下拖动。
再次执行FixedUpdate()
时,最终您的代码设置了Update()
,当它返回到_keepOnJumping = true;
时,它将执行FixedUpdate()
,但紧接着又执行另一个第二次执行物理模拟(由于低帧频),在将刚体显示到屏幕上之前再次将其向下拖动。
希望这有助于您了解问题及其发生原因,以便您现在拥有正确解决问题的正确工具。