aboutsummaryrefslogtreecommitdiff
path: root/demos/pong/Pong.hs
blob: b323aa2b7f634a768929116c964ee1d0b9a0f664 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
module Pong
(
    GameEvent(..)
,   GameObject
,   newWorld
,   stepWorld
,   aabb
)
where

import Spear.Math.AABB
import Spear.Math.Spatial2
import Spear.Math.Vector
import Spear.Step

import Data.List (foldl')
import Data.Monoid
import GHC.Float (double2Float)

-- Game events

data GameEvent
     = MoveLeft
     | MoveRight
     | StopLeft
     | StopRight
     deriving Eq

-- Game objects

data GameObject = GameObject
     { aabb   :: AABB2
     , obj    :: Obj2
     , gostep :: Step ([GameEvent], [GameObject], GameObject) GameObject
     }

instance Spatial2 GameObject where
         getObj2 = obj
         setObj2 s o = s { obj = o }

stepWorld :: Elapsed -> Dt -> [GameEvent] -> [GameObject] -> [GameObject]
stepWorld elapsed dt evts gos = map (update elapsed dt evts gos) gos

update :: Elapsed -> Dt -> [GameEvent] -> [GameObject] -> GameObject -> GameObject
update elapsed dt evts gos go =
       let (go', s') = step (gostep go) elapsed dt (evts, gos, go)
       in go' { gostep = s' }

ballBox :: AABB2
ballBox = AABB2 (vec2 (-s) (-s)) (vec2 s s) where s = 0.01

padSize = vec2 0.05 0.02

padBox = AABB2 (-padSize) padSize

obj2 x y = obj2FromVectors unitx2 unity2 (vec2 x y)

ballVelocity = Vector2 0.3 0.3

newWorld =
         [ GameObject ballBox (obj2 0.5 0.5) $ stepBall ballVelocity
         , GameObject padBox  (obj2 0.5 0.9) stepEnemy
         , GameObject padBox  (obj2 0.5 0.1) stepPlayer
         ]

-- Generic steppers

ignore :: Step ([GameEvent], [GameObject], GameObject) GameObject
ignore = spure $ \(_,_,go) -> go

ignoreEvts :: Step ([GameEvent], [GameObject], GameObject) ([GameObject], GameObject)
ignoreEvts = spure $ \(_, world, go) -> (world, go)

ignoreGOs :: Step ([GameEvent], [GameObject], GameObject) ([GameEvent], GameObject)
ignoreGOs = spure $ \(evts, _, go) -> (evts, go)

-- Ball steppers

stepBall vel = ignoreEvts .> collideBall vel .> moveBall

collideBall :: Vector2 -> Step ([GameObject], GameObject) (Vector2, GameObject)
collideBall vel = Step $ \_ _ (gos, ball) ->
            let (AABB2 pmin pmax) = aabb ball `aabbAdd` pos ball
                collideCol = x pmin < 0 || x pmax > 1
                collideRow = y pmin < 0 || y pmax > 1
                           || any (collide ball) (tail gos)
                negx v@(Vector2 x y) = if collideCol then vec2 (-x) y else v
                negy v@(Vector2 x y) = if collideRow then vec2 x (-y) else v
                vel' = negx . negy $ vel
            in ((vel', ball), collideBall vel')

collide go1 go2 =
        let (AABB2 (Vector2 xmin1 ymin1) (Vector2 xmax1 ymax1))
                   = aabb go1 `aabbAdd` pos go1
            (AABB2 (Vector2 xmin2 ymin2) (Vector2 xmax2 ymax2))
                   = aabb go2 `aabbAdd` pos go2
        in not $  xmax1 < xmin2 || xmin1 > xmax2
               || ymax1 < ymin2 || ymin1 > ymax2

aabbAdd (AABB2 pmin pmax) p = AABB2 (p+pmin) (p+pmax)

moveBall :: Step (Vector2, GameObject) GameObject
moveBall = Step $ \_ dt (vel,ball) -> (move (scale dt vel) ball, moveBall)

-- Enemy stepper

stepEnemy = ignore .> movePad

movePad :: Step GameObject GameObject
movePad = Step $ \elapsed _ pad ->
        let p  = vec2 px 0.9
            px = double2Float (sin elapsed * 0.5 + 0.5)
               * (1 - 2 * x padSize)
               + x padSize
        in (setPos p pad, movePad)

-- Player stepper

stepPlayer = ignoreGOs
           .> moveGO False MoveLeft StopLeft
           .> moveGO False MoveRight StopRight
           .> ssnd
           .> clamp

moveGO :: Bool -> GameEvent -> GameEvent
       -> Step ([GameEvent], GameObject) ([GameEvent], GameObject)
moveGO moving start stop = Step $ \_ dt (evts, go) ->
       let moving' = (moving || any (==start) evts) && not (any (==stop) evts)
           dir = scale dt $ toDir moving' start
       in ((evts, move dir go), moveGO moving' start stop)

clamp :: Step GameObject GameObject
clamp = spure $ \go ->
      let p' = vec2 (clamp' x s (1 - s)) y
          (Vector2 x y) = pos go
          clamp' x a b = if x < a then a else if x > b then b else x
          (Vector2 s _) = padSize
      in setPos p' go

toDir True MoveLeft  = vec2 (-1) 0
toDir True MoveRight = vec2 1 0
toDir _ _ = vec2 0 0