7

Haskellを使用して生成されたいくつかのデータの非常に迅速で汚いアニメーション表示を取得しようとしています。試すのが最も簡単なのはアスキーアートのようです。言い換えると、次のようなものです。

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

type Animation = [Frame]

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

どうすればこれを行うのが最善ですか?

私がまったく理解できない部分は、フレーム間の一時停止を最小限に抑える方法です。残りは、この回答で見つかったANSIターミナルputStrLnパッケージと一緒に使用するのが簡単です。clearScreen

4

2 に答える 2

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は、名前が示すように、ヘルツ単位のフレームレート、つまり1秒あたりのフレーム数です。このrunFrames関数は、最初に各フレームを描画するアクションに変換し、次に各フレームをaddDelay関数に渡します。関数は、アクションの実行前後の時間をチェックし、フレーム時間が経過するまでスリープします。

私自身のコードはこれとは少し異なって見えます。これは、通常、イベントのSDLのポーリング、バックグラウンド処理の実行、次の反復へのデータの受け渡しなど、他の処理を行うより複雑なループがあるためです。しかし、基本的な考え方は同じです。

明らかに、このアプローチの良いところは、かなりシンプルでありながら、可能な場合は一貫したフレームレートが得られ、ターゲット速度を明確に指定できることです。

于 2011-09-16T23:18:18.387 に答える
2

これはCAMcCannの答えに基づいており、特にフレームレートがティックレートの整数分数でない場合は、うまく機能しますが、長期的には時間安定性がありません。

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
于 2011-09-18T18:59:16.920 に答える