Netwire 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, указывающий, что произошло столкновение. В связанной статье используется программа "vult", которая в текущей версии Netwire (AFAIK) исчезла. И это не слишком красиво - интегрировать вручную, когда для этого есть провод... Я знаю, что я могу ограничить положение, используя IntegrationWith, но он не может создать ничего, кроме дробного. Я пытался так:

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)

И прости меня за это;). Теперь я знаю, что в интегральном проводе есть внутреннее состояние, которое я не изменяю таким образом.

Так каков "правильный путь" для достижения того, чего я хочу?

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, все, что я сделал, это возился с типами, чтобы получить его Работа.

мой 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 или нет, или это вообще вообще полезно, или есть более простой способ, который я еще не видел,

Вы можете сделать это с существующим 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))
Другие вопросы по тегам