Is there a way in elm (or rather, what is the proper way) to stop and start the Time.fps
function based on the state of the application (stored in the model)? For example, in an application I'm working on, when the pause key is pressed, the state field of the model is set to Paused. I would like the fps to stop until it is set to Playing again. My initial thought was to use Time.fpsWhen
, but it throws an error. I've thrown together this example which is structurally similar to my application to demonstrate.
import Signal
import Time
import Keyboard
import Text
import Graphics.Element (Element)
---- MODELS ----
type State = Running | Stopped
type Action = ToggleRunning | DeltaTime Float
type alias Model = {state : State, counter : Float}
---- UPDATE ----
update : Action -> Model -> Model
update action model = case action of
DeltaTime dt -> { model | counter <- model.counter + dt }
ToggleRunning -> { model | state <- if model.state == Running then Stopped else Running }
---- VIEW ----
view : Model -> Element
view model = Text.asText model
---- INPUTS ----
stateToggleKey : Signal Action
stateToggleKey = Signal.map (always ToggleRunning) <| Signal.keepIf identity False Keyboard.space
timeDelta : Signal Action
timeDelta = Signal.map DeltaTime <| Time.fpsWhen 20 (Signal.map (.state >> (==) Running) model)
input : Signal Action
input = Signal.merge stateToggleKey timeDelta
model : Signal Model
model = Signal.foldp update {state=Running, counter=0} input
main : Signal Element
main = Signal.map view model
You can see it "running" here.
My assumption is that it isn't working because model
and timeDelta
are recursively dependent on each other, but I'm not sure. In any case, how should I go about stopping the fps while the application is in a paused state? Do I have to store the running state separately from the model?
Thanks!
In general recursively defined signals are not supported. The compiler just isn't good enough to be able to check for wrongly defined recursive values yet. You may want to look at a related question/answer on SO about recursive definition: https://stackoverflow.com/a/22284451/859279
If your game can only be paused by spacebar, and only be unpaused by some input that isn't dependent on the game state, you can actually use fpsWhen
:
onDown s = keepIf ((==) True) True s
playing = Signal.merge (always False <~ onDown Keyboard.spacebar) (always True <~ onDown Keyboard.enter)
-- turn the two arguments to merge around for the playing signal to start with True
-- another way where spacebar toggles pausing:
playing = Signal.foldp (\_ b -> not b) False (onDown Keyboard.spacebar)
timeDeltas = Time.fpsWhen 60 playing
inputs = (,,) <~ timeDeltas ~ playing ~ etc
state = Signal.foldp update startState inputs
main = view <~ state