Blender Game Engine 中 IF 函数的意外行为

Unexpected behavior with the IF function in Blender Game Engine

我正在 BGE 中做一个项目,我想让一个物体在我按下按钮时慢慢加速到它的最大速度。当我按下另一个按钮时,它应该减速,然后在另一个方向上再次缓慢加速到最高速度。

代码如下:

import bge

cont = bge.logic.getCurrentController()
own = cont.owner
keyboard = bge.logic.keyboard
ACTIVE = bge.logic.KX_INPUT_ACTIVE
INACTIVE = bge.logic.KX_INPUT_NONE
accelerate = own['accelerateProp']

accelerateFactor = 0.005

own.applyMovement((0, accelerate, 0))

if(keyboard.events[bge.events.WKEY] == ACTIVE and accelerate > -0.05):
    own['accelerateProp'] -= accelerateFactor

if(keyboard.events[bge.events.SKEY] == ACTIVE and accelerate < 0.05):
    own['accelerateProp'] += accelerateFac

我有一个带有 accelerateProp 属性 的对象,我用它来控制对象的速度。

当我按住 W 键时它会加速。但是它没有在 -0.05 处停止,而是再运行一次并在 -0.055 处停止。如果我然后按住 S 按钮,它会减速然后再次加速并停在 0.05。

令人困惑的是,如果我先按住 S 键,它会加速到 0.055,反过来它会正常工作。

所以实际上,首先达到最高速度的方向会被破坏,而另一个会很好,并且会保持这种状态,所以一侧的最大速度始终为 0.055,另一侧的最大速度为 0.05。

我不明白哪里出了问题。我怀疑它必须做一些机智的游戏属性,但我真的不知道。另外,如果有其他方法可以做我所做的事情,请告诉我。

谢谢!

没有更多信息很难确定,但我几乎可以肯定这只是一个典型的浮点舍入错误。

考虑这段代码(它实际上与您的代码执行相同的操作,但中间没有所有用户交互内容):

x = 0
while x < 0.05:
    x += 0.005
print(x)

您认为结果会是 0.05,对吧?但事实并非如此。是 0.05499999999999999。为什么一步过头了?

如果一路打印出所有的值,原因很明显:

0.005
0.01
0.015
0.02
0.025
0.030000000000000002
0.035
0.04
0.045
0.049999999999999996
0.05499999999999999

没有完全等于所有这些数字的浮点双精度数,因此您累积了舍入误差。到第10个的时候,不是0.05,是0.049999999999999996,还差0.05,所以又往前走了一步


像这样的问题有两种解决方法。


首先,您可以使用 Decimal 而不是 float。当然 Decimal 通常与 float 一样不精确并且容易出现舍入误差;不同之处在于,任何具有精确十进制字符串表示形式的数字(如 0.005 和所有其他值)也具有精确的 Decimal 表示形式。所以:

from decimal import Decimal
x = Decimal('0')
while x < Decimal('0.05'):
    x += Decimal('0.005')
print(x)

现在你得到 0.05 而不是 0.05499999999999999


或者,您可以使用绝对或相对 epsilon 比较。例如:

eps = 1e-5

while x + eps < 0.05:
    x += 0.005
print(x)

现在你得到 0.049999999999999996 而不是最后的 0.05499999999999999

问题的原因是浮点数的舍入误差。不幸的是,对于浮点数,加法和乘法会引入小错误,从而导致比较出现问题。

相反,您应该使用 "clamping",这意味着您通过与阈值比较来防止 over/undershooting,然后将该值设置为阈值本身:

acceleration = -0.05 if braking else 0.05
speed = max(0, min(max_speed, speed + acceleration))

因此速度永远不会超过[0..max_speed]

的界限

四舍五入你的 属性。 prop = round(prop["value"], 2) 2是圆点。