haskell中的简单弹跳球不断弹跳得更高

haskell中的简单弹跳球不断弹跳得更高,haskell,simulation,physics,Haskell,Simulation,Physics,所以,我做了一个简单的弹跳球模拟来学习haskell。球应该在图像边界上反弹,并在重力作用下向下加速 问题是,随着时间的推移,球“神奇地”反弹得更高。我希望它能保持相同的最大高度 我怀疑反弹和移动都有问题,因为反弹将球“传送”回帧内,这在物理上并不准确。我尝试了不同的方法来模拟正确的行为,但找不到任何正确的方法 正确的方法是什么 以下是代码,运行于: 假设球在时间量子的中点正好击中地面。在现实中(或者更确切地说,在无摩擦的“现实”中),速度的绝对值在量子开始和结束时保持不变,但在你的模型中它会增

所以,我做了一个简单的弹跳球模拟来学习haskell。球应该在图像边界上反弹,并在重力作用下向下加速

问题是,随着时间的推移,球“神奇地”反弹得更高。我希望它能保持相同的最大高度

我怀疑
反弹
移动
都有问题,因为
反弹
将球“传送”回帧内,这在物理上并不准确。我尝试了不同的方法来模拟正确的行为,但找不到任何正确的方法

正确的方法是什么

以下是代码,运行于:


假设球在时间量子的中点正好击中地面。在现实中(或者更确切地说,在无摩擦的“现实”中),速度的绝对值在量子开始和结束时保持不变,但在你的模型中它会增加

人们应该在
弹跳中处理加速度。最简单的方法是:

move t (Ball (x,y) (vx,vy)) = Ball (x + vx*t, y + vy*t) (vx, vy)

step t world = bounce (move t world) t
bounce (Ball (x,y) (vx,vy)) t = Ball (nx,ny) (nvx, nvy) 
   ...
   nvy = if ny /= y then -vy else vy + g * t
在反弹过程中,速度不会增加


这仍然不完全准确,速度仍在缓慢上升,但速度较慢。

这是用于积分运动微分方程的算法的一个众所周知的伪影

“真正的物理学”是(我只讨论y分量)

∂y/∂t=v(t)
∂五/∂t=g

通过高度和速度的离散序列对此进行建模

yi=yi− 1+vi− 1.⋅ Δt
vi=vi− 1+g⋅ Δt

这当然类似于微分方程,只是写为差分商——但它不是一回事(除了极限Δt)→ 0):实际上,速度本身在时间步长期间会发生变化,因此仅根据该时间步长之前的恒定v值来改变位置是不正确的。简单地忽略这一复杂情况就是所谓的近似值,众所周知,它相当糟糕

更准确的标准替代方案是四阶,试一试


n、 m.关于弹跳的观点也是有效的,尽管要正确地计算撞击的准确时间,你应该计算撞击的准确时间,既不要忽略太多的加速度,也不要得到太多实际上从未发生过的加速度。

虽然弹跳确实是个问题(尽管积分算法的选择更糟糕),这不是解决问题的好办法。重力加速度和反弹有什么关系?这个函数当然不应该再这样调用了。最好的方法可能是将积分器作为参数传递给
bounce
,但只在
步骤
中将其全部内联也可以。如果我们关心真实的行为而不关心数值准确性,这对于反弹问题来说是一个“足够好”的解决方案。我认为积分仍然需要类似Runge Kutta的东西。当然Runge Kutta会很好地工作,问题是如何保持模拟非常简单,即使牺牲数值精度,但保持系统的明显特性,如能量守恒。正如@n.m.所说,我更想得到一些看起来真实的东西,而不是一些真正准确的东西。Runge-Kutta仍然是一个很好的调用,因为它实现起来并不复杂,而且是可重用的。
move t (Ball (x,y) (vx,vy)) = Ball (x + vx*t, y + vy*t) (vx, vy)

step t world = bounce (move t world) t
bounce (Ball (x,y) (vx,vy)) t = Ball (nx,ny) (nvx, nvy) 
   ...
   nvy = if ny /= y then -vy else vy + g * t