我已经阅读了这个和官方文档:fixedUpdate()和深层解释。
所以我试图分离我的代码。首先,在Update()
,我没有给出完整的代码,变量是不言自明的:
private void Update()
{
if (Input.GetButton("Jump")) {
if (groundsTouched>0) {
_jumpRequest = true;
} else {
_keepOnJumping = true;
}
} else {
_keepOnJumping = false;
}
/* Handle release button: */
_fallRequest = true;
}
Run Code Online (Sandbox Code Playgroud)
现在我正在做这样的所有计算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;
}
}
Run Code Online (Sandbox Code Playgroud)
我遇到的问题真的很奇怪:FPS低时,玩家跳不高。
Unity QA 看到了我的问题,他们的回答是:
您正在添加依赖于 fixedDeltaTime 的 Force,这取决于您的可用性能(或本质上是帧速率)。
如果您转到 Edit->Project Settings->Time 并将固定 Timestep 更改为更大的值,您将获得预期的行为。尝试几个不同的固定时间步长值,看看行为如何变化。
另一个建议是重写代码,使其不依赖于帧速率(例如,使用速度而不是强制或在跳跃时添加特定量的力,不依赖于时间步长)。
“另一个建议是重写代码,使其不依赖于帧速率”-> 你会怎么做,我认为我上面的代码正在这样做!
我错过了什么?我做错了什么/可能的解决方案是什么?
我想知道给你这个答案的 Unity QA 是谁,这太可怕了。
让我解释一下现实中会发生什么。
1)让我们开始fixedDeltaTime
:这个值永远不会依赖于帧速率。它可以在编辑器 (in Edit->Project Settings->Time
) 中设置,这是在运行时保留的值,除非任何脚本通过赋值更改它。Unity 引擎永远不会自行更改它。
2) 物理循环:在完整的引擎循环中,Unity 将执行多个物理循环(0,1 或更多),然后执行单个渲染循环。每个渲染循环执行的物理循环数基于此数量fixedDeltaTime
以及自上次deltaTime
循环以来经过的时间(即:渲染循环的时间)。
例如,假设fixedDeltaTime = 0.0166667
自上次物理循环以来经过的时间小于该时间,例如10
ms。Unity 不会执行物理循环。现在假设甚至下一帧都以10
毫秒为单位进行渲染- 这意味着自上次物理循环以来,20
毫秒已经过去了。由于这高于fixedDeltaTime
,Unity 将执行物理循环。有时可能会发生帧渲染非常缓慢的情况(由于不可预见的原因),例如在40
ms 中。为了保持物理模拟的一致性,Unity 必须连续运行两个物理循环,这是因为0.04/0.0166667 = 2.4
.
请记住,Unity 会跟踪上一个物理循环开始时间和下一个物理循环之间的差异:如果渲染10
每帧持续ms,并且fixedDeltaTime
设置为166667
ms (60Hz),则只要您启动运行时,Unity 就会执行第一个物理循环,然后在第一个渲染帧之后跳过一个(因为只有10
ms 已经传递而不是166667
),然后在第二个渲染帧(20
针对 传递的 ms )之后执行一个物理循环166667
。但是现在我们的循环被3.3333
ms去同步了,所以 Unity 会跟踪它。
在第 3 帧之后,又过了10
ms,但不会执行物理循环,因为10+3.3333 = 13.3333
它仍然低于fixedDeltaTime
. 现在,让我们假设第 4 个渲染帧“出错”,它持续25
ms 而不仅仅是10
。在下一个物理循环开始时,25+13.3333 = 38.3333
自上次物理更新以来总共经过了 和38.3333/16.6667 = 2.3
,并且 Unity 将连续执行两个物理循环以跟上固定步长模拟,然后继续渲染第 5 帧。
在所有这些介绍之后,让我们回到你的问题,看看会发生什么:
在某个时刻执行Update()
, 并设置_jumpRequest = true;
和_fallRequest = true;
。
在这个渲染帧之后,FixedUpdate()
第一次执行,执行AddForce ForceMode.VelocityChange
线,设置_fallRequest = false;
,_jumpGravitySent = false;
和_keepOnJumping = false;
。在此结束后FixedUpdate()
,Unity 执行物理模拟,借助物理引擎调整刚体的位置和速度。
现在问题触发了:由于渲染帧很慢,物理循环至少连续执行两次,但Update()
中间没有执行,所以FixedUpdate()
跳过所有内容,但物理模拟运行了第二次,拖动刚体相对于预期的最高位置向下的位置。
当Update()
再次执行,最后你的代码集_keepOnJumping = true;
,当它回来给FixedUpdate()
它会执行AddForce ForceMode.Acceleration
,但紧接着,另一种物理模拟是第2次(因低帧速率)执行,再拖动收到刚体下来可以在屏幕上渲染。
希望这有助于了解您的问题及其发生原因,以便您现在拥有正确的工具来正确修复它。