New generic player implementation

This commit is contained in:
Matthias Schiffer 2010-03-02 21:36:37 +01:00
parent d4db00ce9b
commit 8586ef7b85
4 changed files with 129 additions and 62 deletions

35
DefaultPlayer.hs Normal file
View file

@ -0,0 +1,35 @@
{-# LANGUAGE DeriveDataTypeable, PatternGuards #-}
module DefaultPlayer ( DefaultPlayer(..)
) where
import qualified Data.Set as S
import Data.Fixed
import Data.Ratio ((%))
import Data.Typeable
import GLDriver
import Player
import Tank
data DefaultPlayer = DefaultPlayer (S.Set Key)
deriving (Typeable, Show)
instance Player DefaultPlayer where
playerMovement (DefaultPlayer keys) _ = playerMovement' keys
handleEvent (DefaultPlayer keys) ev
| Just (KeyPressEvent key) <- fromEvent ev = DefaultPlayer $ S.insert key keys
| Just (KeyReleaseEvent key) <- fromEvent ev = DefaultPlayer $ S.delete key keys
| otherwise = DefaultPlayer keys
playerMovement' :: S.Set Key -> (DefaultPlayer, Maybe Micro, Bool)
playerMovement' keys = (DefaultPlayer keys, angle, move)
where
x = (if (S.member KeyLeft keys) then (-1) else 0) + (if (S.member KeyRight keys) then 1 else 0)
y = (if (S.member KeyDown keys) then (-1) else 0) + (if (S.member KeyUp keys) then 1 else 0)
move = (x /= 0 || y /= 0)
angle = if move then Just $ fromRational $ round ((atan2 y x)*1000000*180/pi)%1000000 else Nothing

129
HTanks.hs
View file

@ -4,6 +4,8 @@ import Game
import Level import Level
import Render import Render
import Tank import Tank
import Player
import DefaultPlayer
import GLDriver import GLDriver
import GLX import GLX
@ -22,7 +24,7 @@ data MainState = MainState
{ run :: !Bool { run :: !Bool
, driver :: !SomeDriver , driver :: !SomeDriver
, time :: !UTCTime , time :: !UTCTime
, keyset :: !(S.Set Key) , players :: ![SomePlayer]
} }
newtype MainT m a = MainT (StateT MainState m a) newtype MainT m a = MainT (StateT MainState m a)
@ -40,7 +42,7 @@ main = do
when (initialized gl) $ do when (initialized gl) $ do
currentTime <- getCurrentTime currentTime <- getCurrentTime
let mainState = MainState {run = True, driver = SomeDriver gl, time = currentTime, keyset = S.empty} let mainState = MainState {run = True, driver = SomeDriver gl, time = currentTime, players = [SomePlayer $ DefaultPlayer S.empty]}
gameState = GameState {level = testLevel, tanks = [Tank 0.0 0.0 0 0 2 360 False], textures = M.empty} gameState = GameState {level = testLevel, tanks = [Tank 0.0 0.0 0 0 2 360 False], textures = M.empty}
runGame gameState $ do runGame gameState $ do
@ -80,82 +82,87 @@ mainLoop = do
when runnext mainLoop when runnext mainLoop
playerMovement :: Main (Maybe Micro, Bool)
playerMovement = do updateAngle :: Micro -> State Tank ()
keys <- gets keyset updateAngle angle = do
oldangle <- gets dir
tspeed <- gets turnspeed >>= return . (/1000)
let x = (if (S.member KeyLeft keys) then (-1) else 0) + (if (S.member KeyRight keys) then 1 else 0) let diff = angle - oldangle
y = (if (S.member KeyDown keys) then (-1) else 0) + (if (S.member KeyUp keys) then 1 else 0) let diff360 = if (diff > 180)
then (diff-360)
else if (diff <= -180)
then (diff+360)
else diff
if (x /= 0 || y /= 0) let (diff180, angle180) = if (diff360 > 90)
then return (Just $ fromRational $ round ((atan2 y x)*1000000*180/pi)%1000000, True) then (diff360-180, oldangle+180)
else return (Nothing, False) else if (diff360 <= -90)
then (diff360+180, oldangle-180)
else (diff360, oldangle)
let turn = if (diff180 > tspeed)
then tspeed
else if (diff180 < -tspeed)
then (-tspeed)
else diff180
let newangle = angle180 + turn
let newangle180 = if (newangle > 180)
then (newangle-360)
else if (newangle <= -180)
then (newangle+360)
else newangle
modify $ \tank -> tank {dir = newangle180}
updateTank :: Maybe Micro -> Bool -> State Tank ()
updateTank angle move = do
when (isJust angle) $
updateAngle $ fromJust angle
when move $ do
tdir <- gets dir
tspeed <- gets speed
moved <- gets moving
when (isNothing angle || (isJust angle && (tdir == fromJust angle)) || moved) $ do
let anglej = (fromRational . toRational $ tdir)*pi/180
x = tspeed * fromRational (round ((cos anglej)*1000)%1000000)
y = tspeed * fromRational (round ((sin anglej)*1000)%1000000)
modify $ \tank -> tank {posx = x + posx tank, posy = y + posy tank, moving = True}
when (not move) $ do
modify $ \tank -> tank {moving = False}
simulationStep :: Main () simulationStep :: Main ()
simulationStep = do simulationStep = do
(angle, move) <- playerMovement oldplayers <- gets players
oldtanks <- lift $ gets tanks
when (isJust angle) $ do let pt = unzip $ map updateTank' $ zip oldplayers oldtanks
tank <- lift $ gets (head . tanks)
let oldangle = dir tank
let diff = fromJust angle - oldangle
let diff360 = if (diff > 180)
then (diff-360)
else if (diff <= -180)
then (diff+360)
else diff
let (diff180, angle180) = if (diff360 > 90)
then (diff360-180, oldangle+180)
else if (diff360 <= -90)
then (diff360+180, oldangle-180)
else (diff360, oldangle)
let tspeed = (turnspeed tank)/1000
let turn = if (diff180 > tspeed)
then tspeed
else if (diff180 < -tspeed)
then (-tspeed)
else diff180
let newangle = angle180 + turn
let newangle180 = if (newangle > 180)
then (newangle-360)
else if (newangle <= -180)
then (newangle+360)
else newangle
lift $ modify $ \state -> state {tanks = (tank {dir = newangle180}):(tail . tanks $ state)}
when move $ do modify $ \state -> state {players = fst pt}
tank <- lift $ gets (head . tanks) lift $ modify $ \state -> state {tanks = snd pt}
let moved = moving tank where
updateTank' (player, tank) = let (p, angle, move) = playerMovement player tank
when (isNothing angle || (isJust angle && (dir tank == fromJust angle)) || moved) $ do t = execState (updateTank angle move) tank
let angle = (fromRational . toRational $ dir tank)*pi/180 in (p, t)
x = (speed tank) * fromRational (round ((cos angle)*1000)%1000000)
y = (speed tank) * fromRational (round ((sin angle)*1000)%1000000)
lift $ modify $ \state -> state {tanks = (tank {posx = x + posx tank, posy = y + posy tank, moving = True}):(tail . tanks $ state)}
when (not move) $ do
tank <- lift $ gets (head . tanks)
lift $ modify $ \state -> state {tanks = (tank {moving = False}):(tail . tanks $ state)}
handleEvents :: Main () handleEvents :: Main ()
handleEvents = do handleEvents = do
event <- gets driver >>= liftIO . nextEvent event <- gets driver >>= liftIO . nextEvent
when (isJust event) $ do when (isJust event) $ do
handleEvent $ fromJust event Main.handleEvent $ fromJust event
modify $ \state -> state {players = map (\p -> Player.handleEvent p $ fromJust event) $ players state}
handleEvents handleEvents
handleEvent :: SomeEvent -> Main () handleEvent :: SomeEvent -> Main ()
handleEvent ev handleEvent ev
| Just (ResizeEvent w h) <- fromEvent ev = lift $ resize w h | Just (ResizeEvent w h) <- fromEvent ev = lift $ resize w h
| Just (KeyPressEvent key) <- fromEvent ev = modify $ \state -> state {keyset = S.insert key (keyset state)}
| Just (KeyReleaseEvent key) <- fromEvent ev = modify $ \state -> state {keyset = S.delete key (keyset state)}
| Just QuitEvent <- fromEvent ev = modify $ \state -> state {run = False} | Just QuitEvent <- fromEvent ev = modify $ \state -> state {run = False}
| otherwise = return () | otherwise = return ()

View file

@ -1,5 +1,5 @@
HSCFILES = Bindings/GLX.hsc Bindings/GLPng.hsc HSCFILES = Bindings/GLX.hsc Bindings/GLPng.hsc
HSFILES = $(HSCFILES:%.hsc=%.hs) GLDriver.hs GLX.hs Texture.hs Tank.hs Level.hs Game.hs Render.hs HTanks.hs HSFILES = $(HSCFILES:%.hsc=%.hs) GLDriver.hs GLX.hs Texture.hs Tank.hs Player.hs DefaultPlayer.hs Level.hs Game.hs Render.hs HTanks.hs
all: HTanks all: HTanks

25
Player.hs Normal file
View file

@ -0,0 +1,25 @@
{-# LANGUAGE ExistentialQuantification, DeriveDataTypeable #-}
module Player ( Player(..)
, SomePlayer(..)
) where
import Data.Fixed
import Data.Typeable
import Tank
import GLDriver (SomeEvent)
class Player a where
playerMovement :: a -> Tank -> (a, Maybe Micro, Bool)
handleEvent :: a -> SomeEvent -> a
handleEvent player _ = player
data SomePlayer = forall a. Player a => SomePlayer a
instance Player SomePlayer where
playerMovement (SomePlayer player) tank = (\(p, angle, move) -> (SomePlayer p, angle, move)) $ playerMovement player tank
handleEvent (SomePlayer player) event = SomePlayer $ handleEvent player event