aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Demos/Pong/Main.hs26
-rw-r--r--Demos/Pong/Pong.hs85
-rw-r--r--Spear.cabal6
-rw-r--r--Spear/App.hs7
-rw-r--r--Spear/Math/Spatial.hs12
-rw-r--r--Spear/Step.hs65
-rw-r--r--Spear/Window.hs44
7 files changed, 120 insertions, 125 deletions
diff --git a/Demos/Pong/Main.hs b/Demos/Pong/Main.hs
index 0237a26..21fcb0c 100644
--- a/Demos/Pong/Main.hs
+++ b/Demos/Pong/Main.hs
@@ -14,6 +14,7 @@ import Spear.Render.Core.State
14import Spear.Render.Immediate 14import Spear.Render.Immediate
15import Spear.Window 15import Spear.Window
16 16
17import Control.Monad (when)
17import Data.Maybe (mapMaybe) 18import Data.Maybe (mapMaybe)
18 19
19 20
@@ -28,7 +29,7 @@ data GameState = GameState
28app = App step render resize 29app = App step render resize
29 30
30main = 31main =
31 withWindow (900, 600) (Just "Pong") initGame endGame $ 32 withWindow (1920, 1200) (Just "Pong") initGame endGame $
32 loop app 33 loop app
33 34
34initGame :: Window -> Game () GameState 35initGame :: Window -> Game () GameState
@@ -44,13 +45,22 @@ endGame = do
44step :: Elapsed -> Dt -> [InputEvent] -> Game GameState Bool 45step :: Elapsed -> Dt -> [InputEvent] -> Game GameState Bool
45step elapsed dt inputEvents = do 46step elapsed dt inputEvents = do
46 gs <- getGameState 47 gs <- getGameState
47 let events = translateEvents inputEvents 48 events <- processInput (window gs)
49 --when (events /= []) $ gameIO . putStrLn $ show events
48 modifyGameState $ \gs -> 50 modifyGameState $ \gs ->
49 gs 51 gs
50 { world = stepWorld (realToFrac elapsed) (realToFrac dt) events (world gs) 52 { world = stepWorld (realToFrac elapsed) (realToFrac dt) events (world gs)
51 } 53 }
52 return (not $ exitRequested inputEvents) 54 return (not $ exitRequested inputEvents)
53 55
56processInput :: Window -> Game GameState [GameEvent]
57processInput window = processKeys window
58 [ (KEY_A, MoveLeft)
59 , (KEY_D, MoveRight)
60 ]
61
62exitRequested = elem (KeyDown KEY_ESC)
63
54render :: Game GameState () 64render :: Game GameState ()
55render = do 65render = do
56 gameState <- getGameState 66 gameState <- getGameState
@@ -79,7 +89,7 @@ renderBackground =
79 let pmin = 0 :: Float 89 let pmin = 0 :: Float
80 pmax = 1 :: Float 90 pmax = 1 :: Float
81 in do 91 in do
82 immSetColour (vec4 0.6 0.35 0.6 1.0) 92 immSetColour (vec4 0.0 0.25 0.41 1.0)
83 immDrawQuads2d [ 93 immDrawQuads2d [
84 (vec2 pmin pmin 94 (vec2 pmin pmin
85 ,vec2 pmax pmin 95 ,vec2 pmax pmin
@@ -98,7 +108,6 @@ renderGO go = do
98 ,vec2 xmax ymax 108 ,vec2 xmax ymax
99 ,vec2 xmin ymax)] 109 ,vec2 xmin ymax)]
100 110
101-- TODO: Fix the resize hang.
102resize :: WindowEvent -> Game GameState () 111resize :: WindowEvent -> Game GameState ()
103resize (ResizeEvent w h) = 112resize (ResizeEvent w h) =
104 let r = fromIntegral w / fromIntegral h 113 let r = fromIntegral w / fromIntegral h
@@ -112,12 +121,3 @@ resize (ResizeEvent w h) =
112 modifyGameState $ \state -> state { 121 modifyGameState $ \state -> state {
113 viewProjection = Matrix4.ortho left right bottom top (-1) 1 122 viewProjection = Matrix4.ortho left right bottom top (-1) 1
114 } 123 }
115
116translateEvents = mapMaybe translateEvents'
117 where translateEvents' (KeyDown KEY_LEFT) = Just MoveLeft
118 translateEvents' (KeyDown KEY_RIGHT) = Just MoveRight
119 translateEvents' (KeyUp KEY_LEFT) = Just StopLeft
120 translateEvents' (KeyUp KEY_RIGHT) = Just StopRight
121 translateEvents' _ = Nothing
122
123exitRequested = elem (KeyDown KEY_ESC)
diff --git a/Demos/Pong/Pong.hs b/Demos/Pong/Pong.hs
index 104a92e..dd8855b 100644
--- a/Demos/Pong/Pong.hs
+++ b/Demos/Pong/Pong.hs
@@ -24,13 +24,14 @@ import Data.Monoid (mconcat)
24 24
25-- Configuration 25-- Configuration
26 26
27padSize = vec2 0.07 0.02 27padSize = vec2 0.07 0.015
28ballSize = 0.012 :: Float 28ballSize = 0.012 :: Float
29ballSpeed = 0.6 :: Float 29ballSpeed = 0.7 :: Float
30initialBallVelocity = vec2 1 1 30initialBallVelocity = vec2 1 1
31maxBounceAngle = (65::Float) * (pi::Float)/(180::Float) 31maxBounceAngle = (65::Float) * (pi::Float)/(180::Float)
32playerSpeed = 1.0 :: Float 32playerSpeed = 1.0 :: Float
33enemySpeed = 3.0 :: Float 33enemySpeed = 7.0 :: Float
34enemyMomentum = 0.1 :: Float
34initialEnemyPos = vec2 0.5 0.9 35initialEnemyPos = vec2 0.5 0.9
35initialPlayerPos = vec2 0.5 0.1 36initialPlayerPos = vec2 0.5 0.1
36initialBallPos = vec2 0.5 0.5 37initialBallPos = vec2 0.5 0.5
@@ -40,9 +41,7 @@ initialBallPos = vec2 0.5 0.5
40data GameEvent 41data GameEvent
41 = MoveLeft 42 = MoveLeft
42 | MoveRight 43 | MoveRight
43 | StopLeft 44 deriving (Eq, Ord, Show)
44 | StopRight
45 deriving (Eq, Ord)
46 45
47-- Game objects 46-- Game objects
48 47
@@ -79,17 +78,9 @@ instance Spatial GameObject Vector2 Angle Transform2 where
79 transform = basis 78 transform = basis
80 79
81 80
82stepWorld :: Elapsed -> Dt -> [GameEvent] -> [GameObject] -> [GameObject]
83stepWorld elapsed dt evts gos = map (update elapsed dt evts gos) gos
84
85update :: Elapsed -> Dt -> [GameEvent] -> [GameObject] -> GameObject -> GameObject
86update elapsed dt evts gos go =
87 let (go', s') = runStep (gostep go) elapsed dt gos evts go
88 in go' {gostep = s'}
89
90ballBox, padBox :: AABB2 81ballBox, padBox :: AABB2
91ballBox = AABB2 (vec2 (-s) (-s)) (vec2 s s) where s = ballSize 82ballBox = AABB2 (vec2 (-s) (-s)) (vec2 s s) where s = ballSize
92padBox = AABB2 (-padSize) padSize 83padBox = AABB2 (-padSize) padSize
93 84
94newWorld = 85newWorld =
95 [ GameObject ballBox (makeAt initialBallPos) $ stepBall initialBallVelocity, 86 [ GameObject ballBox (makeAt initialBallPos) $ stepBall initialBallVelocity,
@@ -98,23 +89,30 @@ newWorld =
98 ] 89 ]
99 where makeAt = newTransform2 unitx2 unity2 90 where makeAt = newTransform2 unitx2 unity2
100 91
92
93stepWorld :: Elapsed -> Dt -> [GameEvent] -> [GameObject] -> [GameObject]
94stepWorld elapsed dt evts gos = map (update elapsed dt evts gos) gos
95
96update :: Elapsed -> Dt -> [GameEvent] -> [GameObject] -> GameObject -> GameObject
97update elapsed dt evts gos go =
98 let (go', s') = runStep (gostep go) elapsed dt gos evts go
99 in go' {gostep = s'}
100
101-- Ball steppers 101-- Ball steppers
102 102
103stepBall vel = collideBall vel .> moveBall 103stepBall vel = collideBall vel .> moveBall
104 104
105-- TODO: in collideBall and paddleBounce, we should an apply an offset to the
106-- ball when collision is detected.
107collideBall :: Vector2 -> Step [GameObject] e GameObject (Vector2, GameObject) 105collideBall :: Vector2 -> Step [GameObject] e GameObject (Vector2, GameObject)
108collideBall vel = step $ \_ dt gos _ ball -> 106collideBall vel = step $ \_ dt gos _ ball ->
109 let (AABB2 pmin pmax) = translate (position ball) (aabb ball) 107 let (AABB2 pmin pmax) = translate (position ball) (aabb ball)
110 collideSide = x pmin < 0 || x pmax > 1 108 sideCollision = x pmin < 0 || x pmax > 1
111 collideBack = y pmin < 0 || y pmax > 1 109 backCollision = y pmin < 0 || y pmax > 1
112 collidePaddle = any (collide ball) (tail gos) 110 flipX v@(Vector2 x y) = if sideCollision then vec2 (-x) y else v
113 flipX v@(Vector2 x y) = if collideSide then vec2 (-x) y else v 111 flipY v@(Vector2 x y) = if backCollision then vec2 x (-y) else v
114 flipY v@(Vector2 x y) = if collideBack then vec2 x (-y) else v
115 vel' = normalise . (\v -> foldl (paddleBounce ball) v (tail gos)) . flipX . flipY $ vel 112 vel' = normalise . (\v -> foldl (paddleBounce ball) v (tail gos)) . flipX . flipY $ vel
116 -- A small delta to apply when collision occurs. 113 collision = vel' /= vel
117 delta = (1::Float) + if collideSide || collideBack || collidePaddle then (2::Float)*dt else (0::Float) 114 -- Apply offset when collision occurs to avoid sticky collisions.
115 delta = (1::Float) + if collision then (3::Float)*dt else (0::Float)
118 in ((ballSpeed * delta * vel', ball), collideBall vel') 116 in ((ballSpeed * delta * vel', ball), collideBall vel')
119 117
120paddleBounce :: GameObject -> Vector2 -> GameObject -> Vector2 118paddleBounce :: GameObject -> Vector2 -> GameObject -> Vector2
@@ -139,37 +137,38 @@ collide go1 go2 =
139 (AABB2 (Vector2 xmin2 ymin2) (Vector2 xmax2 ymax2)) = 137 (AABB2 (Vector2 xmin2 ymin2) (Vector2 xmax2 ymax2)) =
140 translate (position go2) (aabb go2) 138 translate (position go2) (aabb go2)
141 in not $ 139 in not $
142 xmax1 < xmin2 140 xmax1 < xmin2 ||
143 || xmin1 > xmax2 141 xmin1 > xmax2 ||
144 || ymax1 < ymin2 142 ymax1 < ymin2 ||
145 || ymin1 > ymax2 143 ymin1 > ymax2
146 144
147moveBall :: Step s e (Vector2, GameObject) GameObject 145moveBall :: Step s e (Vector2, GameObject) GameObject
148moveBall = step $ \_ dt _ _ (vel, ball) -> (translate (vel * dt) ball, moveBall) 146moveBall = step $ \_ dt _ _ (vel, ball) -> (translate (vel * dt) ball, moveBall)
149 147
150-- Enemy stepper 148-- Enemy stepper
151 149
152stepEnemy = movePad 150stepEnemy = movePad 0 .> clamp
153 151
154movePad :: Step s e GameObject GameObject 152movePad :: Float -> Step [GameObject] e GameObject GameObject
155movePad = step $ \elapsed _ _ _ pad -> 153movePad previousMomentum = step $ \_ dt gos _ pad ->
156 let enemyY = 0.9 154 let ball = head gos
157 p = vec2 px enemyY 155 offset = (x . position $ ball) - (x . position $ pad)
158 px = 156 chaseVector = enemySpeed * offset
159 (sin (enemySpeed * elapsed) * (0.5::Float) + (0.5::Float)) 157 momentum = previousMomentum + enemyMomentum * chaseVector
160 * ((1::Float) - (2::Float) * x padSize) 158 vx = chaseVector + momentum
161 + x padSize 159 in (translate (vec2 (vx * dt) 0) pad, movePad momentum)
162 in (setPosition p pad, movePad) 160
161sign :: Float -> Float
162sign x = if x >= 0 then 1 else -1
163 163
164-- Player stepper 164-- Player stepper
165 165
166stepPlayer = sfold moveGO .> clamp 166stepPlayer = sfold moveGO .> clamp
167 167
168moveGO = 168moveGO = mconcat
169 mconcat 169 [ swhen MoveLeft $ moveGO' (vec2 (-playerSpeed) 0)
170 [ switch StopLeft sid MoveLeft (moveGO' $ vec2 (-playerSpeed) 0), 170 , swhen MoveRight $ moveGO' (vec2 playerSpeed 0)
171 switch StopRight sid MoveRight (moveGO' $ vec2 playerSpeed 0) 171 ]
172 ]
173 172
174moveGO' :: Vector2 -> Step s e GameObject GameObject 173moveGO' :: Vector2 -> Step s e GameObject GameObject
175moveGO' dir = step $ \_ dt _ _ go -> (translate (dir * dt) go, moveGO' dir) 174moveGO' dir = step $ \_ dt _ _ go -> (translate (dir * dt) go, moveGO' dir)
diff --git a/Spear.cabal b/Spear.cabal
index b044ae2..c327cf9 100644
--- a/Spear.cabal
+++ b/Spear.cabal
@@ -129,12 +129,14 @@ library
129 129
130 hs-source-dirs: . 130 hs-source-dirs: .
131 131
132 ghc-options: -O2 132 ghc-options: -O2 -g
133 133
134 ghc-prof-options: -O2 -fprof-auto -fprof-cafs 134 ghc-prof-options: -O2 -g -fprof-auto -fprof-cafs
135 135
136executable pong 136executable pong
137 hs-source-dirs: Demos/Pong 137 hs-source-dirs: Demos/Pong
138 main-is: Main.hs 138 main-is: Main.hs
139 other-modules: Pong 139 other-modules: Pong
140 build-depends: base, Spear, OpenGL 140 build-depends: base, Spear, OpenGL
141 ghc-options: -O2 -g
142 ghc-prof-options: -O2 -g -fprof-auto -fprof-cafs
diff --git a/Spear/App.hs b/Spear/App.hs
index f70dd06..b0c7141 100644
--- a/Spear/App.hs
+++ b/Spear/App.hs
@@ -57,16 +57,19 @@ loop' ::
57loop' window ddt inputTimer elapsed timeBudget app = do 57loop' window ddt inputTimer elapsed timeBudget app = do
58 timer <- gameIO $ tick inputTimer 58 timer <- gameIO $ tick inputTimer
59 59
60 inputEvents <- gameIO $ pollInputEvents window
61
60 let timeBudgetThisFrame = timeBudget + deltaTime timer 62 let timeBudgetThisFrame = timeBudget + deltaTime timer
61 let steps = timeBudgetThisFrame `div` ddt 63 let steps = timeBudgetThisFrame `div` ddt
62 64
65 --gameIO . putStrLn $ "Steps: " ++ show steps
66
63 continue <- and <$> forM [1..steps] (\i -> do 67 continue <- and <$> forM [1..steps] (\i -> do
64 let t = timeDeltaToSec $ elapsed + i * ddt 68 let t = timeDeltaToSec $ elapsed + i * ddt
65 let dt = timeDeltaToSec ddt 69 let dt = timeDeltaToSec ddt
66 inputEvents <- gameIO $ pollInputEvents window
67 stepApp app t dt inputEvents) 70 stepApp app t dt inputEvents)
68 71
69 let elapsed' = elapsed + steps * ddt 72 let elapsed' = elapsed + steps * ddt
70 let timeBudget' = timeBudgetThisFrame `mod` ddt 73 let timeBudget' = timeBudgetThisFrame `mod` ddt
71 74
72 when continue $ do 75 when continue $ do
diff --git a/Spear/Math/Spatial.hs b/Spear/Math/Spatial.hs
index bfab6c2..b6a3ede 100644
--- a/Spear/Math/Spatial.hs
+++ b/Spear/Math/Spatial.hs
@@ -88,22 +88,22 @@ class (Positional a v, Rotational a v r) => Spatial a v r t | a -> t where
88move :: Positional a v => Float -> (a -> v) -> a -> a 88move :: Positional a v => Float -> (a -> v) -> a -> a
89move delta axis a = translate (axis a * delta) a 89move delta axis a = translate (axis a * delta) a
90 90
91-- | Move the spatial upwards. 91-- | Move the spatial along its right axis.
92moveRight delta = move delta right 92moveRight delta = move delta right
93 93
94-- | Move the spatial downwards. 94-- | Move the spatial along its left axis.
95moveLeft delta = moveRight (-delta) 95moveLeft delta = moveRight (-delta)
96 96
97-- | Move the spatial upwards. 97-- | Move the spatial along its up axis.
98moveUp delta = move delta up 98moveUp delta = move delta up
99 99
100-- | Move the spatial downwards. 100-- | Move the spatial along its down axis.
101moveDown delta = moveUp (-delta) 101moveDown delta = moveUp (-delta)
102 102
103-- | Move the spatial forwards. 103-- | Move the spatial along its forward axis.
104moveFwd delta = move delta forward 104moveFwd delta = move delta forward
105 105
106-- | Move the spatial backwards. 106-- | Move the spatial along its backward axis.
107moveBack delta = moveFwd (-delta) 107moveBack delta = moveFwd (-delta)
108 108
109-- | Make the spatial look at the given point. 109-- | Make the spatial look at the given point.
diff --git a/Spear/Step.hs b/Spear/Step.hs
index cb4f71c..a860247 100644
--- a/Spear/Step.hs
+++ b/Spear/Step.hs
@@ -21,6 +21,7 @@ module Spear.Step
21 (.>), 21 (.>),
22 (<.), 22 (<.),
23 szip, 23 szip,
24 swhen,
24 switch, 25 switch,
25 multiSwitch, 26 multiSwitch,
26 ) 27 )
@@ -36,8 +37,8 @@ type Elapsed = Float
36type Dt = Float 37type Dt = Float
37 38
38-- | A step function. 39-- | A step function.
39newtype Step state events input a = Step 40newtype Step state events a b = Step
40 { runStep :: Elapsed -> Dt -> state -> events -> input -> (a, Step state events input a) 41 { runStep :: Elapsed -> Dt -> state -> events -> a -> (b, Step state events a b)
41 } 42 }
42 43
43instance Functor (Step s e a) where 44instance Functor (Step s e a) where
@@ -73,29 +74,12 @@ ssnd = spure snd
73 74
74-- | Construct a step that folds a given list of inputs. 75-- | Construct a step that folds a given list of inputs.
75-- 76--
76-- The step is run N+1 times, where N is the size of the input list. 77-- The step is run once per input, or not at all if the list is empty.
77sfold :: Step s (Maybe e) a a -> Step s [e] a a 78sfold :: Step s (Maybe e) a a -> Step s [e] a a
78sfold s = Step $ \elapsed dt g es a -> 79sfold s = Step $ \elapsed dt g es a ->
79 case es of 80 let (a', s') = foldl' f (a, s) es
80 [] -> 81 f (a, s) e = runStep s elapsed dt g (Just e) a
81 let (b', s') = runStep s elapsed dt g Nothing a 82 in (a', sfold s')
82 in (b', sfold s')
83 es ->
84 let (b', s') = sfold' elapsed dt g s a es
85 in (b', sfold s')
86
87sfold' ::
88 Elapsed ->
89 Dt ->
90 s ->
91 Step s (Maybe e) a a ->
92 a ->
93 [e] ->
94 (a, Step s (Maybe e) a a)
95sfold' elapsed dt g s a = foldl' f (a', s')
96 where
97 f (a, s) e = runStep s elapsed dt g (Just e) a
98 (a', s') = runStep s elapsed dt g Nothing a
99 83
100-- Combinators 84-- Combinators
101 85
@@ -117,9 +101,22 @@ szip f (Step s1) (Step s2) = Step $ \elapsed dt g e d ->
117 (b, s2') = s2 elapsed dt g e d 101 (b, s2') = s2 elapsed dt g e d
118 in (f a b, szip f s1' s2') 102 in (f a b, szip f s1' s2')
119 103
120-- | Construct a step that switches between two steps based on input. 104-- | Construct a step that is executed when the given event occurs.
105swhen :: Eq e => e -> Step s () a a -> Step s (Maybe e) a a
106swhen expectedEvent step = Step $ \elapsed dt state maybeEvent a ->
107 case maybeEvent of
108 Nothing -> (a, swhen expectedEvent step)
109 Just event ->
110 if event == expectedEvent
111 then let (a', step') = runStep step elapsed dt state () a
112 in (a', swhen expectedEvent step')
113 else (a, swhen expectedEvent step)
114
115-- | Construct a step that switches between two steps based on input events.
116--
117-- The current step runs with every 'runStep' even when there are no new events.
121-- 118--
122-- The initial step is the first one. 119-- The initial step is the identity, 'sid'.
123switch :: 120switch ::
124 Eq e => 121 Eq e =>
125 e -> 122 e ->
@@ -127,16 +124,8 @@ switch ::
127 e -> 124 e ->
128 Step s (Maybe e) a a -> 125 Step s (Maybe e) a a ->
129 Step s (Maybe e) a a 126 Step s (Maybe e) a a
130switch flag1 s1 flag2 s2 = switch' s1 flag1 s1 flag2 s2 127switch = switch' sid
131 128
132switch' ::
133 Eq e =>
134 Step s (Maybe e) a a ->
135 e ->
136 Step s (Maybe e) a a ->
137 e ->
138 Step s (Maybe e) a a ->
139 Step s (Maybe e) a a
140switch' cur flag1 s1 flag2 s2 = Step $ \elapsed dt g e a -> 129switch' cur flag1 s1 flag2 s2 = Step $ \elapsed dt g e a ->
141 case e of 130 case e of
142 Nothing -> 131 Nothing ->
@@ -146,11 +135,15 @@ switch' cur flag1 s1 flag2 s2 = Step $ \elapsed dt g e a ->
146 let next 135 let next
147 | e' == flag1 = s1 136 | e' == flag1 = s1
148 | e' == flag2 = s2 137 | e' == flag2 = s2
149 | otherwise = cur 138 | otherwise = cur
150 (a', s') = runStep next elapsed dt g e a 139 (a', s') = runStep next elapsed dt g e a
151 in (a', switch' s' flag1 s1 flag2 s2) 140 in (a', switch' s' flag1 s1 flag2 s2)
152 141
153-- | Construct a step that switches among multiple steps based on input. 142-- | Construct a step that switches among multiple steps based on input events.
143--
144-- The current step runs with every 'runStep' even when there are no new events.
145--
146-- The initial step is the identity, 'sid'.
154multiSwitch :: (Eq e, Ord e) => [(e, Step s (Maybe e) a a)] -> Step s (Maybe e) a a 147multiSwitch :: (Eq e, Ord e) => [(e, Step s (Maybe e) a a)] -> Step s (Maybe e) a a
155multiSwitch xs = multiSwitch' Nothing sid (Map.fromList xs) 148multiSwitch xs = multiSwitch' Nothing sid (Map.fromList xs)
156 149
diff --git a/Spear/Window.hs b/Spear/Window.hs
index 3cdc5f5..be52080 100644
--- a/Spear/Window.hs
+++ b/Spear/Window.hs
@@ -127,10 +127,11 @@ setup (w, h) windowTitle = do
127 127
128 GLFW.makeContextCurrent maybeWindow 128 GLFW.makeContextCurrent maybeWindow
129 129
130 GLFW.swapInterval 1 -- Enable vsync.
131
130 GLFW.setWindowCloseCallback window . Just $ onWindowClose closeRequest 132 GLFW.setWindowCloseCallback window . Just $ onWindowClose closeRequest
131 GLFW.setWindowSizeCallback window . Just $ onResize windowEvents 133 GLFW.setWindowSizeCallback window . Just $ onResize windowEvents
132 GLFW.setKeyCallback window . Just $ onKey inputEvents 134 GLFW.setKeyCallback window . Just $ onKey inputEvents
133 GLFW.setCharCallback window . Just $ onChar inputEvents
134 GLFW.setMouseButtonCallback window . Just $ onMouseButton inputEvents 135 GLFW.setMouseButtonCallback window . Just $ onMouseButton inputEvents
135 GLFW.setCursorPosCallback window . Just $ onMouseMove mousePos inputEvents 136 GLFW.setCursorPosCallback window . Just $ onMouseMove mousePos inputEvents
136 137
@@ -178,19 +179,16 @@ onWindowClose closeRequest window = putMVar closeRequest True
178-- the last in a poll can be ignored, we just replace the contents of the mvar 179-- the last in a poll can be ignored, we just replace the contents of the mvar
179-- here instead of adding the event to the list. 180-- here instead of adding the event to the list.
180onResize :: MVar [WindowEvent] -> GLFW.WindowSizeCallback 181onResize :: MVar [WindowEvent] -> GLFW.WindowSizeCallback
181onResize windowEvents window w h = putMVar windowEvents [ResizeEvent w h] 182onResize windowEvents window w h = modifyMVar_ windowEvents (return <$> const [ResizeEvent w h])
182 183
183onKey :: MVar [InputEvent] -> GLFW.KeyCallback 184onKey :: MVar [InputEvent] -> GLFW.KeyCallback
184onKey events window key _ GLFW.KeyState'Pressed _ = addEvent events $ KeyDown (fromGLFWkey key) 185onKey events window key _ GLFW.KeyState'Pressed _ = addEvent events $ KeyDown (fromGLFWkey key)
185onKey events window key _ GLFW.KeyState'Released _ = addEvent events $ KeyUp (fromGLFWkey key) 186onKey events window key _ GLFW.KeyState'Released _ = addEvent events $ KeyUp (fromGLFWkey key)
186onKey events window key _ GLFW.KeyState'Repeating _ = return () 187onKey events window key _ GLFW.KeyState'Repeating _ = return ()
187 188
188onChar :: MVar [InputEvent] -> GLFW.CharCallback
189onChar events window char = addEvent events $ KeyDown . fromGLFWkey . read $ [char]
190
191onMouseButton :: MVar [InputEvent] -> GLFW.MouseButtonCallback 189onMouseButton :: MVar [InputEvent] -> GLFW.MouseButtonCallback
192onMouseButton events window button GLFW.MouseButtonState'Pressed _ = addEvent events $ MouseDown (fromGLFWbutton button) 190onMouseButton events window button GLFW.MouseButtonState'Pressed _ = addEvent events $ MouseDown (fromGLFWbutton button)
193onMouseButton events window button GLFW.MouseButtonState'Released _ = addEvent events $ MouseUp (fromGLFWbutton button) 191onMouseButton events window button GLFW.MouseButtonState'Released _ = addEvent events $ MouseUp (fromGLFWbutton button)
194 192
195onMouseMove :: MVar MousePos -> MVar [InputEvent] -> GLFW.CursorPosCallback 193onMouseMove :: MVar MousePos -> MVar [InputEvent] -> GLFW.CursorPosCallback
196onMouseMove oldPos events window x y = do 194onMouseMove oldPos events window x y = do
@@ -208,45 +206,45 @@ replaceMVar mvar val = tryTakeMVar mvar >> putMVar mvar val
208addEvent :: MVar [a] -> a -> IO () 206addEvent :: MVar [a] -> a -> IO ()
209addEvent mvar val = 207addEvent mvar val =
210 tryTakeMVar mvar >>= \xs -> case xs of 208 tryTakeMVar mvar >>= \xs -> case xs of
211 Nothing -> putMVar mvar [val] 209 Nothing -> putMVar mvar [val] -- >> (putStrLn $ show val)
212 Just events -> putMVar mvar (val : events) 210 Just events -> putMVar mvar (val : events) -- >> (putStrLn $ show (val:events))
213 211
214-- Input 212-- Input
215 213
216-- | Run the game action when the key is down. 214-- | Run the game action when the key is down.
217whenKeyDown :: GLFW.Window -> Key -> Game s a -> Game s () 215whenKeyDown :: GLFW.Window -> Key -> Game s a -> Game s ()
218whenKeyDown = whenKeyInState (== GLFW.KeyState'Pressed) 216whenKeyDown = whenKeyInState GLFW.KeyState'Pressed
219 217
220-- | Run the game action when the key is up. 218-- | Run the game action when the key is up.
221whenKeyUp :: GLFW.Window -> Key -> Game s a -> Game s () 219whenKeyUp :: GLFW.Window -> Key -> Game s a -> Game s ()
222whenKeyUp = whenKeyInState (== GLFW.KeyState'Released) 220whenKeyUp = whenKeyInState GLFW.KeyState'Released
223 221
224whenKeyInState :: (GLFW.KeyState -> Bool) -> GLFW.Window -> Key -> Game s a -> Game s () 222whenKeyInState :: GLFW.KeyState -> GLFW.Window -> Key -> Game s a -> Game s ()
225whenKeyInState pred window key game = do 223whenKeyInState state window key game = do
226 isDown <- fmap pred $ gameIO . GLFW.getKey window . toGLFWkey $ key 224 isDown <- fmap (==state) $ gameIO . GLFW.getKey window . toGLFWkey $ key
227 when isDown $ void game 225 when isDown $ void game
228 226
229-- | Process the keyboard keys, returning those values for which their 227-- | Check whether the given keys are pressed and return the value associated
230-- corresponding key is pressed. 228-- with each of the pressed keys.
231processKeys :: GLFW.Window -> [(Key, a)] -> Game s [a] 229processKeys :: Window -> [(Key, a)] -> Game s [a]
232processKeys window = foldM f [] 230processKeys window = foldM f []
233 where 231 where
234 f acc (key, result) = do 232 f acc (key, result) = do
235 isDown <- 233 isDown <-
236 fmap (== GLFW.KeyState'Pressed) $ 234 fmap (== GLFW.KeyState'Pressed) $
237 gameIO . GLFW.getKey window . toGLFWkey $ 235 gameIO . GLFW.getKey (glfwWindow window) . toGLFWkey $
238 key 236 key
239 return $ if isDown then result : acc else acc 237 return $ if isDown then result : acc else acc
240 238
241-- | Process the mouse buttons, returning those values for which their 239-- | Check whether the given buttons are pressed and return the value associated
242-- corresponding button is pressed. 240-- with each of the pressed buttons.
243processButtons :: GLFW.Window -> [(MouseButton, a)] -> Game s [a] 241processButtons :: Window -> [(MouseButton, a)] -> Game s [a]
244processButtons window = foldM f [] 242processButtons window = foldM f []
245 where 243 where
246 f acc (button, result) = do 244 f acc (button, result) = do
247 isDown <- 245 isDown <-
248 fmap (== GLFW.MouseButtonState'Pressed) $ 246 fmap (== GLFW.MouseButtonState'Pressed) $
249 gameIO . GLFW.getMouseButton window . toGLFWbutton $ 247 gameIO . GLFW.getMouseButton (glfwWindow window) . toGLFWbutton $
250 button 248 button
251 return $ if isDown then result : acc else acc 249 return $ if isDown then result : acc else acc
252 250