helm-0.6.0: src/FRP/Helm/Time.hs
{-| Contains functions for composing units of time and signals that sample from the game clock. -}
module FRP.Helm.Time (
-- * Types
Time,
-- * Composing
millisecond,
second,
minute,
hour,
inMilliseconds,
inSeconds,
inMinutes,
inHours,
fps,
-- * Clock State
running,
delta,
delay
) where
import Control.Applicative
import FRP.Elerea.Simple hiding (delay)
import qualified Graphics.UI.SDL as SDL
{-| A type describing an amount of time in an arbitary unit. Use the time composing/converting functions to manipulate
time values. -}
type Time = Double
{-| A time value representing one millisecond. -}
millisecond :: Time
millisecond = 1
{-| A time value representing one second. -}
second :: Time
second = 1000
{-| A time value representing one minute. -}
minute :: Time
minute = 60000
{-| A time value representing one hour. -}
hour :: Time
hour = 3600000
{-| Converts a time value to a fractional value, in milliseconds. -}
inMilliseconds :: Time -> Double
inMilliseconds n = n
{-| Converts a time value to a fractional value, in seconds. -}
inSeconds :: Time -> Double
inSeconds n = n / second
{-| Converts a time value to a fractional value, in minutes. -}
inMinutes :: Time -> Double
inMinutes n = n / minute
{-| Converts a time value to a fractional value, in hours. -}
inHours :: Time -> Double
inHours n = n / hour
{-| Converts a frames-per-second value into a time value. -}
fps :: Int -> Time
fps n = second / realToFrac n
{-| A signal that returns the time that the game has been running for when sampled. -}
running :: SignalGen (Signal Time)
running = effectful $ (*) millisecond <$> realToFrac <$> SDL.getTicks
{-| A signal that returns the time since it was last sampled when sampled. -}
delta :: SignalGen (Signal Time)
delta = running >>= delta'
{-| A utility function that does the real magic for 'delta'. -}
delta' :: Signal Time -> SignalGen (Signal Time)
delta' t = (fmap . fmap) ((*) millisecond . snd) $ transfer (0, 0) (\t2 (t1, _) -> (t2, t2 - t1)) t
{-| A signal that blocks the game thread for a certain amount of time when sampled and then returns the
amount of time it blocked for. Please note that delaying by values smaller than 1 millisecond can have
platform-specific results. -}
delay :: Time -> SignalGen (Signal Time)
delay t = effectful $ do
before <- SDL.getTicks
SDL.delay fixed
(*) millisecond <$> realToFrac <$> flip (-) before <$> SDL.getTicks
where
fixed = max 0 $ round $ inMilliseconds t