编辑:要回答这个问题,是的,您使用您正在使用的近似值是正确的,它是求解一阶微分方程的欧拉方法,并且对于您的目的来说足够准确,特别是因为用户没有周围角速度的绝对值可以用来判断你。减少时间间隔会使它更准确,但这并不重要。
您可以用更少、更大的步骤来完成此操作(见下文),但这种方式对我来说似乎是最清晰的,我希望对您来说也是如此。
为什么要费心这个更长的解决方案呢?即使当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) 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)