1

我正在尝试制作一个 n 体求解器(一堆相互引力吸引的物体)。问题是它看起来像gravity1函数没有反馈返回的对象,导致对象的线性运动:

代码如下所示:

    updateGame :: Game -> SF AppInput Game
    updateGame game = 
      proc input -> do
        ...
        objs        <- updateObjects' $ _foreground (Game._objects game) -< ()
    
        ...
    updateObjects' :: [Object] -> SF () [Object]
    updateObjects' objs =  parB . fmap (updateObject1 objs ) $ objs
    
    updateObject1 :: [Object] -> Object -> SF () Object
    updateObject1 objs0 obj0 =
      proc () -> do
        obj  <- gravity1 (objs0, obj0) -< ()
        returnA -< obj
    
    g = 6.673**(-11.0) :: Double
    
    gravity1 :: ([Object], Object) -> SF () (Object)    
    gravity1 (objs0, obj0) =
      proc () -> do
        let
          m0     =  _mass obj0               :: Double
          xform0 = (head . _transforms) obj0 :: M44 Double
          p0     = ( view (_w._xyz)) xform0  :: V3 Double
          
          ms     = foldr1 (+) $ fmap (_mass) objs0              :: Double
          xforms = fmap (head . _transforms) objs0              :: [M44 Double]
          ps     = foldr1 (^+^) $ fmap ( view (_w._xyz)) xforms :: V3 Double
    
          dir  = ps ^-^ p0                 :: V3 Double
          dist = norm dir                  :: Double
          f    = g * m0 * ms / dist**2.0   :: Double
          acc  = (f / ms) *^ (dir ^/ dist) :: V3 Double
          s    = 1000000000000000.0
          
        --vel <- ((_velocity obj0) ^+^) ^<< integral -< (s *^ acc)
        vel <- ((_velocity obj0) ^+^) ^<< integral -< (s *^ (DT.trace ("acc :" ++ show (s *^ acc)) $ acc))
    
        let mtx =
              mkTransformationMat
              rot
              tr
              where
                rot = (view _m33 xform0)
                tr  = vel + p0
          
        returnA -< obj0 { _transforms = [mtx]
                        , _velocity   = vel }

运行代码,有我在控制台中读取的输出:

acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN

基本上这些值都是相同的(NaN 是由于对自身计算的对象,我应该解决这个问题,但这不是这里的问题),看起来gravity1 函数没有反馈返回对象,尽管返回值是:

    returnA -< obj0 { _transforms = [mtx]
                    , _velocity   = vel }

结果是对象的线性运动,因为 acc 似乎是一个常数。

我在这里的期望是,在gravity1 :: ([Object], Object) -> SF () (Object)更新并返回对象之后,updateObjects' objs = parB . fmap (updateObject1 objs ) $ objsandupdateObject1 objs0 obj0 = ... returnA -< obj应该导致所有对象都被更新,并且下一个迭代周期应该gravity1 :: ([Object], Object) -> SF () (Object)为对象提供一组更新的对象,以便acc每一帧的值都不同......

我是否误解了这里应该如何运作的逻辑?

4

1 回答 1

2

让我们考虑以下类型gravity1

gravity1 :: ([Object], Object) -> SF () (Object)

这是一个函数,当提供Object一个特定的列表时,它Object会产生一个信号函数。这个信号函数就是所谓的生成器,这意味着它可以提供空输入并不断产生Object输出流。

因此,本质上,gravity1作为两种不同的输入:

  • 它具有静态输入([Object], Object),它得到一次并且永远不变,并且
  • 它有流式输入()(基本上相当于没有流式输入)。

考虑到这一点,一旦提供了它的静态参数,它gravity1就会产生一个恒定的流Object——毕竟,它永远不会收到关于任何Objects 所在位置的更新数据!

为了使输出流动态地响应对象位置的变化,这些位置需要是流式的,而不是静态的。特别是,[Object]输入应该是流式的:

gravity1 :: Object -> SF [Object] Object

另一个输入参数,指定Object我们关心的起始位置,可能不需要流式处理,但确实需要小心处理(毕竟只是一个起始位置)。

但是如果gravity1Objects 的位置作为流参数,你将如何运行它updateObject?您可能需要使用某种形式的延迟,例如:

updateObjects :: [Object] -> SF () [Object]
updateObjects objs0 = proc () -> do
  rec objs  <- iPre objs0 -< objs'
      objs' <- parB (fmap gravity1 objs0) -< objs
  returnA -< objs'

顺便说一句,这种使用延迟的策略(即rec关键字以及iPre类似的东西)正是您需要使用的,gravity1它还可以跟踪您正在计算重力的特定对象的当前位置。

于 2021-01-07T14:59:16.777 回答