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是圆点。
我正在 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是圆点。