Bene, ecco un abbozzo di quello che farei:
import Graphics.UI.SDL.Time (getTicks)
import Control.Concurrent (threadDelay)
type Frame = [[Char]]
type Animation = [Frame]
displayFrame :: Frame -> IO()
displayFrame = mapM_ putStrLn
timeAction :: IO() -> IO Integer
timeAction act = do t <- getTicks
act
t' <- getTicks
return (fromIntegral $ t' - t)
addDelay :: Integer -> IO() -> IO()
addDelay hz act = do dt <- timeAction act
let delay = calcDelay dt hz
threadDelay $ fromInteger delay
calcDelay dt hz = max (frame_usec - dt_usec) 0
where frame_usec = 1000000 `div` hz
dt_usec = dt * 1000
runFrames :: Integer -> Animation -> IO()
runFrames hz frs = mapM_ (addDelay hz . displayFrame) frs
Ovviamente sto usando SDL qui per puro getTicks
, perché è quello che ho usato prima. Sentiti libero di sostituirlo con qualsiasi altra funzione per ottenere l'ora corrente.
Il primo argomento su runFrames
è, come suggerisce il nome, la frequenza fotogrammi in hertz, ovvero fotogrammi al secondo. La funzione runFrames
converte prima ciascun fotogramma in un'azione che lo disegna, quindi assegna ciascuno alla funzione addDelay
, che controlla l'ora prima e dopo l'esecuzione dell'azione, quindi si addormenta fino a quando non è trascorsa l'ora del fotogramma.
Il mio codice sarebbe un po 'diverso da questo, perché in genere avrei un ciclo più complicato che farebbe altro, ad esempio, il polling SDL per eventi, l'elaborazione in background, il passaggio dei dati alla successiva iterazione, & c. Ma l'idea di base è la stessa.
Ovviamente la cosa bella di questo approccio è che, pur essendo abbastanza semplice, si ottiene un frame rate costante quando possibile, con un chiaro mezzo per specificare la velocità target.
fonte
2011-09-16 23:18:18
se si cancella lo schermo si sta per essere sfarfallio. c'è uno zoomer con frattale ascii per haskell, controllalo. –