2011-09-16 37 views
6

我想要獲得使用Haskell生成的一些數據的非常快速和骯髒的動畫顯示。最簡單的嘗試似乎是ASCII藝術 - 換句話說,沿線的東西:從Haskell輸出ascii動畫?

type Frame = [[Char]]  -- each frame is given as an array of characters 

type Animation = [Frame] 

display :: Animation -> IO() 
display = ?? 

我該如何做到最好?

我根本找不到的部分是如何確保幀之間的最小暫停;剩下的是直接使用putStrLn加上ansi-terminal包中的clearScreen,通過this answer找到。

+0

如果你清除屏幕,它會閃爍。 haskell有一個ascii分形縮放,請檢查它。 –

回答

5

嗯,這裏是什麼,我會做一個草圖:

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。但基本的想法是一樣的。

很顯然,這種方法的好處在於,儘管仍然非常簡單,但在可能的情況下,您可以獲得一致的幀速率,並具有指定目標速度的明確方法。

+0

非常感謝!最後,我基本上使用了這個,但是用'getClockTime'替換'getTicks'來'System.Time',以避免下載額外的軟件包。 (Haskell是一種懶惰的語言,對吧?:-)) – PLL

2

這建立在C.A.McCann的anwer上,它很好地工作,但從長遠來看不是時間穩定的,特別是當幀率不是tick值的整數分數時。

import GHC.Word (Word32) 

-- import CAMcCann'sAnswer (Frame, Animation, displayFrame, getTicks, threadDelay) 

atTick :: IO() -> Word32 -> IO() 
act `atTick` t = do 
    t' <- getTicks 
    let delay = max (1000 * (t-t')) 0 
    threadDelay $ fromIntegral delay 
    act 

runFrames :: Integer -> Animation -> IO() 
runFrames fRate frs = do 
    t0 <- getTicks 
    mapM_ (\(t,f) -> displayFrame f `atTick` t) $ timecode fRate32 t0 frs 
    where timecode ν t0 = zip [ t0 + (1000 * i) `div` ν | i <- [0..] ] 
     fRate32 = fromIntegral fRate :: Word32 
+0

啊,很好!在我的大部分實際代碼中,我都有基於時間差異的參數化動畫,因此並不太在意確切的幀速率,這就是爲什麼我沒有想到做這種事情的頂部我的頭。但絕對是離散幀的路。 –