5

我正在尝试了解 FRP 和 Netwire。我最好的实践知识来源是这篇文章,但是它有点过时了,因为它是用 Netwire 4 编写的,而且我使用的是 5.0 版。我想要玩家控制的正方形,它会从屏幕边缘反弹。

根据那篇文章,我有这个:

acceleration :: (Monad m, Monoid e) => Wire s e m (Set SDL.Keysym) Double
acceleration  =  pure (-80) . when (keyDown SDL.SDLK_LEFT)
                    <|> pure 80 . when (keyDown SDL.SDLK_RIGHT)
                    <|> pure 0

velocity :: (Monad m, HasTime t s, Monoid e) => Wire s e m (Double, Bool) Double
velocity = integralWith limit 0
    where limit collision v = let newV = if collision then -v else v in clampMax maxV newV


challenge2 :: (MonadFix m, HasTime t s) => Wire s () m (Set SDL.Keysym) Double
challenge2 = proc keysDown -> do
    a <- acceleration -< keysDown
    rec v <- velocity -< (a, colls)
        (pos, colls) <- position -< v
    returnA -< pos


position :: (Monad m, HasTime t s, Monoid e) => Wire s e m Double (Double, Bool)
position = what to put here?

我希望位置线整合速度,纠正位置以保持在屏幕范围内并产生 Bool 指示发生碰撞。链接文章使用 accumT,在当前版本的 Netwire 中,(AFAIK)已经消失。而且它不太漂亮 - 当有一根电线时手动集成......我知道,我可以使用integralWith限制位置,但它只能产生分数。我试过这样:

position = mkSF_ bounds . integral 0
    where bounds pos = if trace (show pos) pos > 150 then (149, True) else if pos < 0 then (1, True) else (pos, False)

请原谅我;)。现在我知道整线有一个内部状态,我不会这样修改。

那么实现我想要的“正确方法”是什么?

4

2 回答 2

2

我一直在关注同一篇文章,也尝试将其翻译为 Netwire 5.0。这确实有点棘手。我最终创建了一个integralWith'设计类似于的新函数,integralWith但它将单个值作为输入并产生两个值。

integralWith' ::
    (Fractional a, HasTime t s)
    => (a -> (a, o))  -- Function for potentially limiting the integration
                      -- and producing a secondary output.
    -> a              -- Integration constant (aka start value).
    -> Wire s e m a (a, o)
integralWith' correct = loop
  where
    loop x' =
        mkPure $ \ds dx ->
            let dt = realToFrac (dtime ds)
                (x,b)  = correct (x' + dt*dx)
            in x' `seq` (Right (x', b), loop x)

这几乎是直接从http://hackage.haskell.org/package/netwire-5.0.0/docs/src/FRP-Netwire-Move.html#integralWith复制的,我所做的只是摆弄类型来获得它工作。

我的position功能最终看起来像这样。

position :: (Monad m, HasTime t s) => Wire s e m Double (Double, Bool)  
position = integralWith' clamp 0    
  where    
    clamp p | p < 0 || p > 150 = (max 1 (min 149 p), True)    
            | otherwise        = (p, False)

由于我自己刚刚进入 FRP 和 Haskell,我不确定 netwire 库中是否已经存在类似的东西,或者它是否普遍有用,或者是否有一种我还没有见过的更简单的方法.

于 2014-03-15T07:47:37.140 回答
0

您可以使用现有integral的 Netwire 执行此操作:

collided :: (Ord a, Num a) => (a, a) -> a -> (a, Bool)
collided (a, b) x
  | x < a = (a, True)
  | x > b = (b, True)
  | otherwise = (x, False)

position :: (Monad m, HasTime t s) => Wire s e m Double (Double, Bool)  
position = integral 0 >>> (arr $ collided (0, 150))
于 2014-05-18T11:43:50.390 回答