编辑:回答这个问题,是的,你使用你正在使用的近似值是正确的,它是欧拉求解一阶微分方程的方法,并且对于你的目的来说足够准确,特别是因为用户没有周围的角速度的绝对值来判断你反对。减少你的时间间隔会使它更准确,但这并不重要。
你可以用更少、更大的步骤来做到这一点(见下文),但这种方式对我来说似乎是最清楚的,我希望你也是。
为什么要打扰这个更长的解决方案?即使eDisplay
以不规则的时间间隔发生,这也有效,因为它计算eDeltaT
.
让我们给自己一个时间事件:
eTime :: Event t Int
eTime = bTime <@ eDisplay
要获得 DeltaT,我们需要跟踪经过的时间间隔:
type TimeInterval = (Int,Int) -- (previous time, current time)
所以我们可以将它们转换为增量:
delta :: TimeInterval -> Int
delta (t0,t1) = t1 - t0
当我们得到一个新的时间间隔时,我们应该如何更新一个时间间隔t2
?
tick :: Int -> TimeInterval -> TimeInterval
tick t2 (t0,t1) = (t1,t2)
因此,让我们将其部分应用于时间,为我们提供一个间隔更新器:
eTicker :: Event t (TimeInterval->TimeInterval)
eTicker = tick <$> eTime
然后我们可以accumE
在初始时间间隔上累积该函数:
eTimeInterval :: Event t TimeInterval
eTimeInterval = accumE (0,0) eTicker
由于 eTime 是从渲染开始开始测量的,因此初始(0,0)
值是合适的。
最后,我们可以通过在时间间隔上应用 ( fmap
ping )来获得 DeltaT 事件。delta
eDeltaT :: Event t Int
eDeltaT = delta <$> eTimeInterval
现在我们需要更新角度,使用类似的想法。
我将制作一个角度更新器,只需将bAngularVelocity
变成一个乘数:
bAngleMultiplier :: Behaviour t (Double->Double)
bAngleMultiplier = (*) <$> bAngularVelocity
然后我们可以用它来制作eDeltaAngle
:(编辑:更改为(+)
并转换为Double
)
eDeltaAngle :: Event t (Double -> Double)
eDeltaAngle = (+) <$> (bAngleMultiplier <@> ((fromInteger.toInteger) <$> eDeltaT)
并累积得到角度:
eAngle :: Event t Double
eAngle = accumE 0.0 eDeltaAngle
如果你喜欢单行,你可以写
eDeltaT = delta <$> (accumE (0,0) $ tick <$> (bTime <@ eDisplay)) where
delta (t0,t1) = t1 - t0
tick t2 (t0,t1) = (t1,t2)
eAngle = accumE 0.0 $ (+) <$> ((*) <$> bAngularVelocity <@> eDeltaT) =
但我不认为这是非常有启发性的,老实说,我不确定我的修复是否正确,因为我没有在 ghci 中测试过。
当然,既然我做了eAngle
而不是bAngle
,你需要
reactimate $ (draw gears) <$> eAngle
而不是你原来的
reactimate $ (draw gears) <$> (bAngle <@ eDisp)