嗯,这里是什么,我会做一个草图:
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
很显然,我使用SDL这里纯粹是为了getTicks
,因为这是我以前使用过。随意将其替换为任何其他功能以获取当前时间。
runFrames
的第一个参数是 - 顾名思义 - 以赫兹为单位的帧速率,即每秒帧数。 runFrames
函数首先将每个帧转换为一个动作,然后将其分配给addDelay
函数,该函数检查运行动作之前和之后的时间,然后睡眠直至帧时间已过。
我自己的代码看起来会有点不同,因为我通常会有一个更复杂的循环来做其他的事情,比如轮询SDL事件,做后台处理,将数据传递给下一次迭代,& C。但基本的想法是一样的。
很显然,这种方法的好处在于,尽管仍然非常简单,但在可能的情况下,您可以获得一致的帧速率,并具有指定目标速度的明确方法。
如果你清除屏幕,它会闪烁。 haskell有一个ascii分形缩放,请检查它。 –