Netwire, keyboard events, and games

I'm using netwire to build a game; one of the things the player can do is move around using WASD. I want to use key *events* as the 'basis' of my wires, not the entire state vector of the keyboard so that, for example, a press event on D increments the velocity by (200, 0) and a release event decrements it by that much. However, this has the problem that I can't actually get the velocity unless I have an event to feed into the wires, which means I can only update the screen when the user presses or releases a key. While this might make for interesting gameplay, it's not what I want. is the right thing to do to make the input something like a Maybe KeyEvent instead, and pass in Nothing whenever I want to get output without an event for, e.g., a render?

Patrick Hurst
I'm using netwire to build a game; one of the things the player can do is move around using WASD. I want to use key *events* as the 'basis' of my wires, not the entire state vector of the keyboard so that, for example, a press event on D increments the velocity by (200, 0) and a release event decrements it by that much. However, this has the problem that I can't actually get the velocity unless I have an event to feed into the wires, which means I can only update the screen when the user presses or releases a key. While this might make for interesting gameplay, it's not what I want.
is the right thing to do to make the input something like a Maybe KeyEvent instead, and pass in Nothing whenever I want to get output without an event for, e.g., a render?
That highly depends on how you want to process the keys. For real-time games you probably want continuous "key held" events instead of instantaneous "key down" and "key up" events. The nicest way here is to use a reader monad below the wire with a set of currently pressed keys: data GameState = GameState { gsKeyDown :: Set Key {- ... -} } type GameWire = WireM (Reader GameState) That way you can write your event wires like identity wires, which makes using them much more convenient. Here is one way to write such an event wire: keyDown :: Key -> GameWire a a keyDown key = mkFixM $ \_ x -> do pressed <- asks (S.member key . gsKeyDown) return (if pressed then Right x else Left mempty) This wire acts like the identity wire when the key is pressed and inhibits otherwise. From such a wire you can easily construct a velocity wire for one direction: direction :: Key -> Double -> GameWire a Double direction key speed = pure speed . keyDown key <|> 0 When the key is held down, this wire has the 'speed' value, otherwise it has the value 0. A one-dimensional velocity is thus just the sum of two of these directions: velocity1 :: Key -> Key -> Double -> GameWire a Double velocity1 upKey downKey speed = direction upKey speed + direction downKey (-speed) A two-dimensional velocity is just a pair of one-dimensional velocities: velocity2 speed = velocity1 W S speed &&& velocity1 A D speed Finally all you need is to turn the velocity into a position. So integrate it: position = integral_ (x0, y0) . velocity2 1 I hope this helps. Greets, Ertugrul -- Not to be or to be and (not to be or to be and (not to be or to be and (not to be or to be and ... that is the list monad.

On Feb 16, 2013, at 18:14, "Ertugrul Söylemez"
Patrick Hurst
wrote: I'm using netwire to build a game; one of the things the player can do is move around using WASD. I want to use key *events* as the 'basis' of my wires, not the entire state vector of the keyboard so that, for example, a press event on D increments the velocity by (200, 0) and a release event decrements it by that much. However, this has the problem that I can't actually get the velocity unless I have an event to feed into the wires, which means I can only update the screen when the user presses or releases a key. While this might make for interesting gameplay, it's not what I want.
is the right thing to do to make the input something like a Maybe KeyEvent instead, and pass in Nothing whenever I want to get output without an event for, e.g., a render?
That highly depends on how you want to process the keys. For real-time games you probably want continuous "key held" events instead of instantaneous "key down" and "key up" events. The nicest way here is to use a reader monad below the wire with a set of currently pressed keys:
data GameState = GameState { gsKeyDown :: Set Key {- ... -} }
type GameWire = WireM (Reader GameState)
That way you can write your event wires like identity wires, which makes using them much more convenient. Here is one way to write such an event wire:
keyDown :: Key -> GameWire a a keyDown key = mkFixM $ \_ x -> do pressed <- asks (S.member key . gsKeyDown) return (if pressed then Right x else Left mempty)
This wire acts like the identity wire when the key is pressed and inhibits otherwise. From such a wire you can easily construct a velocity wire for one direction:
direction :: Key -> Double -> GameWire a Double direction key speed = pure speed . keyDown key <|> 0
When the key is held down, this wire has the 'speed' value, otherwise it has the value 0. A one-dimensional velocity is thus just the sum of two of these directions:
velocity1 :: Key -> Key -> Double -> GameWire a Double velocity1 upKey downKey speed = direction upKey speed + direction downKey (-speed)
A two-dimensional velocity is just a pair of one-dimensional velocities:
velocity2 speed = velocity1 W S speed &&& velocity1 A D speed
Finally all you need is to turn the velocity into a position. So integrate it:
position = integral_ (x0, y0) . velocity2 1
I hope this helps.
Greets, Ertugrul
-- Not to be or to be and (not to be or to be and (not to be or to be and (not to be or to be and ... that is the list monad. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
This is the approach that I currently use; it was pointed out to me that polling the state of the keys on every input is considered bad practice since it means that key clicks that happen in between physics updates don't get registered at all, hence why I wanted to use a more event-driven approach.

Kata Recurse
This is the approach that I currently use; it was pointed out to me that polling the state of the keys on every input is considered bad practice since it means that key clicks that happen in between physics updates don't get registered at all, hence why I wanted to use a more event-driven approach.
The idea of this approach is that the set of currently pressed keys is
constructed by the application loop, not the wire itself. Every event
triggers another step of the wire.
One problem with that approach is that the rendering may be much slower
than a wire step, so you get event congestion and thus delayed
responses. This suggests that it would pay off to handle all events in
a single instant, but that is much more difficult than it sounds. What
should a wire do, if in a single instant a key is pressed and released,
or if it is pressed multiple times?
A simple solution exists: Wires can deal with a time delta of 0, so
step the wire with dt = 0 for all queued events before you render the
next frame. There is nothing wrong with mixing this with sessions. In,
say, SDL terms, if the event is NoEvent, use stepSession* and render.
If it's any other event, use stepWire* and don't render.
Greets,
Ertugrul
--
Key-ID: E5DD8D11 "Ertugrul Soeylemez
participants (3)
-
Ertugrul Söylemez
-
Kata Recurse
-
Patrick Hurst