packages feed

Yampa-0.15: examples/yampa-game/MainBouncingBox.hs

{-# LANGUAGE Arrows #-}
import FRP.Yampa       as Yampa
import Graphics.UI.SDL as SDL

-- Helper functions
import YampaSDL

width :: Num a => a
width  = 640
height :: Num a => a
height = 480

-- | Reactimation.
--
-- This main function runs an FRP system by producing a signal, passing it
-- through a signal function, and consuming it.
--
-- The first two arguments to reactimate are the value of the input signal
-- at time zero and at subsequent times, together with the times between
-- samples.
--
-- The third argument to reactimate is the output consumer that renders
-- the signal.
--
-- The last argument is the actual signal function.
--
main = do
  timeRef <- yampaSDLTimeInit
  reactimate initGraphs
             (\_ -> do
                dtSecs <- yampaSDLTimeSense timeRef
                return (dtSecs, Nothing))
             (\_ e -> display e >> return False)
             (bounce (fromIntegral height / 2) 0)

-- * FRP stuff

-- | Vertical coordinate and velocity of a falling mass starting
-- at a height with an initial velocity.
falling :: Double -> Double -> SF () (Double, Double)
falling y0 v0 = proc () -> do
  vy <- (v0+) ^<< integral -< gravity
  py <- (y0+) ^<< integral -< vy
  returnA -< (py, vy)

-- | Vertical coordinate and velocity of a bouncing mass starting
-- at a height with an initial velicity.
bounce :: Double -> Double -> SF () (Double, Double)
bounce y vy = switch (falling y vy >>> (Yampa.identity &&& hitBottom))
                     (\(y, vy) -> bounce y (-vy))

-- | Fire an event when the input height and velocity indicate
-- that the object has hit the bottom (so it's falling and the
-- vertical position is under the floor).
hitBottom :: SF (Double, Double) (Yampa.Event (Double, Double))
hitBottom = arr (\(y,vy) ->
                  let boxTop = y + fromIntegral boxSide
                  in if (boxTop > fromIntegral height) && (vy > 0)
                       then Yampa.Event (y, vy)
                       else Yampa.NoEvent)

-- * Graphics

-- | Initialise rendering system.
initGraphs :: IO ()
initGraphs = do
  -- Initialise SDL
  SDL.init [InitVideo]

  -- Create window
  screen <- setVideoMode width height 16 [SWSurface]
  setCaption "Test" ""

-- | Display a box at a position.
display :: (Double, Double) -> IO()
display (boxY,_) = do
  -- Obtain surface
  screen <- getVideoSurface

  -- Paint screen green
  let format = surfaceGetPixelFormat screen
  bgColor <- mapRGB format 55 60 64
  fillRect screen Nothing bgColor

  -- Paint small red square, at an angle 'angle' with respect to the center
  foreC <- mapRGB format 212 108 73
  let x = (width - boxSide) `div` 2
      y = round boxY
  fillRect screen (Just (Rect x y boxSide boxSide)) foreC

  -- Double buffering
  SDL.flip screen

gravity :: Double
gravity = 6.2

boxSide :: Int
boxSide = 30