unity3d - 与帧率无关的 FixedUpdate 与 Update
问题描述
我已经阅读了这个和官方文档: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;
}
现在我正在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 QA 看到了我的问题,他们的回答是:
您正在添加 Force 依赖于 fixedDeltaTime ,这取决于您的可用性能(或本质上的帧速率)。
如果您转到 Edit->Project Settings->Time 并将固定 Timestep 更改为更大的值,您将获得预期的行为。尝试几个不同的 Fixed Timestep 值,看看行为如何变化。
另一个建议是重写代码,使其不依赖于帧速率(例如,使用 Velocity 而不是强制或在跳转时添加特定量的力,而不依赖于 Timestep)。
“另一个建议是重写代码,使其不依赖于帧速率” - >你会怎么做,我认为我上面的代码正在这样做!
我错过了什么?我做错了什么/可能是什么解决方案?
解决方案
我想知道给你这个答案的那个 Unity QA 是谁,这太令人畏惧了。
让我解释一下现实中会发生什么。
1)让我们开始fixedDeltaTime
:这个值从不依赖于帧速率。它可以在编辑器中设置(in Edit->Project Settings->Time
),这是在运行时保留的值,除非任何脚本通过赋值更改它。Unity 引擎永远不会自行更改它。
2) 物理循环:在完整的引擎循环中,Unity 将执行多个物理循环(0,1 或更多),然后执行单个渲染循环。每个渲染循环执行的物理循环的数量基于此fixedDeltaTime
以及自上一个循环以来经过了多少时间(即:deltaTime
渲染循环的时间)。
例如,假设fixedDeltaTime = 0.0166667
自上次物理循环以来经过的时间小于该时间,例如10
ms。Unity 不会执行物理循环。现在假设甚至下一帧已经在10
ms 内渲染 - 这意味着自上一次物理循环以来,20
ms 已经过去。由于这高于fixedDeltaTime
,Unity 将执行一个物理循环。有时可能会发生帧渲染非常缓慢(由于不可预见的原因),例如以40
毫秒为单位。为了保持物理模拟的一致性,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
过了一毫秒,但不会执行任何物理循环,因为10+3.3333 = 13.3333
它仍然低于fixedDeltaTime
. 现在,让我们假设第 4 个渲染帧“出错”,并且持续时间25
为 ms 而不是10
. 在下一个 Physics 循环开始时,25+13.3333 = 38.3333
自上次 Physics 更新以来总共经过了38.3333/16.6667 = 2.3
,并且 Unity 将连续执行两个Physics 循环以跟上固定步长模拟,然后继续渲染第 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
,但紧接着,第二次执行另一个物理模拟(由于低帧率),再次将刚体向下拖动到它之前可以在屏幕上渲染。
希望这有助于理解您的问题及其发生的原因,以便您现在拥有正确的工具来正确修复它。
推荐阅读
- scrollbar - 仅对某些元素而不是整个页面启用“-webkit-scrollbar”属性?
- laravel - 我需要在 laravel 中使用 sortBy 进行分页
- reactjs - React Native:无法显示从 Amazon AWS S3 下载的图像
- docker - 在 docker 容器中安装 Sharp 时出错
- r - R Prog- Market Basket Analysis - 如何在使用支持和信心循环时可视化产品推荐?
- javascript - ASP.NET Core MVC 在产品页面详细信息上应用同位素过滤器
- python - 有没有办法允许在 colab 中手动输入的变量在 .py 文件中使用?
- c++ - 从前向迭代器获得的指针和引用
- android - 如何使 localtime.now 成为自动更新的连续函数?
- pandas - 使用 pandas to_csv ,“TypeError:“delimiter”必须是 1 个字符的字符串”。分隔符是 u00BB