Hate-0.1.4.2: samples/sample_asteroids.hs
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleContexts #-}
import Hate
import Hate.Graphics
import Vec2Lens (x,y)
import Control.Applicative
import Control.Lens
import System.Random
-- sample 4
data EntityState = EntityState {
_pos :: Vec2,
_vel :: Vec2,
_rot :: Float,
_rotVel :: Float
}
makeLenses ''EntityState
data Asteroid = Asteroid {
_asteroidEntity :: EntityState,
_size :: Int
}
makeLenses ''Asteroid
makeFields ''Asteroid
data Player = Player {
_playerEntity :: EntityState
}
makeLenses ''Player
makeFields ''Player
data SampleState = SampleState {
_playerSprite :: Sprite,
_smallAsteroidSprite :: Sprite,
_mediumAsteroidSprite :: Sprite,
_bigAsteroidSprite :: Sprite,
_asteroids :: [Asteroid],
_player :: Player
}
makeLenses ''SampleState
data VisualRepresentation = SmallAsteroidSprite | MediumAsteroidSprite | BigAsteroidSprite | PlayerSprite
class ToVisual a where
toVisual :: a -> VisualRepresentation
instance ToVisual Player where
toVisual (Player _) = PlayerSprite
instance ToVisual Asteroid where
toVisual (Asteroid _ sz) | sz < 2 = SmallAsteroidSprite
| sz < 4 = MediumAsteroidSprite
| otherwise = BigAsteroidSprite
randomAsteroids :: Int -> IO [Asteroid]
randomAsteroids n = replicateM n randomAsteroid
randomAsteroid :: IO Asteroid
randomAsteroid = do
px <- getStdRandom $ randomR (0,800)
py <- getStdRandom $ randomR (0,800)
vx <- getStdRandom $ randomR (-2, 2)
vy <- getStdRandom $ randomR (-2, 2)
vr <- getStdRandom $ randomR (-0.1, 0.1)
sz <- getStdRandom $ randomR (1, 5)
let e = EntityState (Vec2 px py) (Vec2 vx vy) 0 vr
return $ Asteroid e sz
initialPlayer :: Player
initialPlayer = Player $ EntityState (Vec2 200 200) (Vec2 0 0) 0 0
sampleLoad :: LoadFn SampleState
sampleLoad = SampleState <$> loadSprite "samples/asteroids/ship.png"
<*> loadSprite "samples/asteroids/asteroid_small.png"
<*> loadSprite "samples/asteroids/asteroid_medium.png"
<*> loadSprite "samples/asteroids/asteroid_big.png"
<*> randomAsteroids 10
<*> pure initialPlayer
sampleDraw :: DrawFn SampleState
sampleDraw s = (map draw (s ^. asteroids)) ++ [draw (s ^. player)]
where
draw :: (HasEntity a EntityState, ToVisual a) => a -> DrawRequest
draw a = (entityToTransform a) . sprite Middle . visualToSprite . toVisual $ a
visualToSprite SmallAsteroidSprite = s ^. smallAsteroidSprite
visualToSprite MediumAsteroidSprite = s ^. mediumAsteroidSprite
visualToSprite BigAsteroidSprite = s ^. bigAsteroidSprite
visualToSprite PlayerSprite = s ^. playerSprite
entityToTransform :: HasEntity a EntityState => a -> DrawRequest -> DrawRequest
entityToTransform a = let e = a ^. entity in ((rotated (e ^. rot)) . (translate (e ^. pos)))
sampleUpdate :: UpdateFn SampleState
sampleUpdate _ = do
asteroids . traversed %= updateEntity
player %= updateEntity
steerShip
where
updateEntity :: HasEntity a EntityState => a -> a
updateEntity a = a & (entity . pos +~ a ^. entity . vel)
& (entity . rot +~ a ^. entity . rotVel)
-- damping:
& (entity . vel *~ 0.99)
& (entity . rotVel *~ 0.99)
& screenWarp
screenWarp a = a & (if a ^. entity . pos . x > 1024 then entity . pos . x -~ 1024 else id)
& (if a ^. entity . pos . y > 768 then entity . pos . y -~ 768 else id)
& (if a ^. entity . pos . x < 0 then entity . pos . x +~ 1024 else id)
& (if a ^. entity . pos . y < 0 then entity . pos . y +~ 768 else id)
steerShip = do
r <- use $ player . entity . rot
whenKeyPressed Key'Up $ player . entity . vel += (rotateVec r (Vec2 0 (negate 1)))
whenKeyPressed Key'Left $ player . entity . rotVel -= 0.01
whenKeyPressed Key'Right $ player . entity . rotVel += 0.01
config :: Config
config =
Config
{ windowTitle = "Sample - Asteroids"
, windowSize = (1024, 768)
}
main :: IO ()
main = runApp config sampleLoad sampleUpdate sampleDraw