Haskell Gloss: переменная времени остается постоянной

Я пытаюсь повернуть изображение в Haskell, используя текущее время в качестве значения для функции поворота. У меня есть следующее main функция:

main :: IO ()
main = do
    args <- getArgs
    time <- round <$> getPOSIXTime
    let initial'        = initial time
    let (w, h, display) = chooseDisplay args
    let background      = black
    let fps             = 60
    play display background fps initial' (draw w h) eventHandler timeHandler

Треугольник (= игрок) хранится в типе данных "Мир": модуль Модель где

data World = World {
        -- all kinds of World properties --
        player           :: Picture
    }

Тогда у меня есть функция initial который инициализирует мир и функцию playerBody который, учитывая значение поворота, возвращает изображение player:

initial :: Int -> World
initial _ = World{player = playerBody 0}

playerBody :: Float -> Picture
playerBody rot = Rotate rot $ color red $ Polygon[(-10, 100), (10, 100), (0, 125)]

Функция draw определяется следующим образом:

draw :: Float -> Float -> World -> Picture
draw _ _ world = player world

В настоящее время он просто возвращает playerКартина.

Теперь, в моем модуле timeHandler, я хочу использовать время (данное timeHandler в main функция) вращаться player следующее:

timeHandler :: Float -> World -> World
timeHandler time = (\world -> world {player = playerBody time} )

Это не работает Я заменил time с постоянным значением (в функции timeHandler), и это вращало изображение. Так вроде time не обновляется.. что я делаю не так?

1 ответ

Решение

Естественно, это не работает, timeHandler получает число, которое на практике близко к дельте времени со времени предыдущего кадра - в документах говорится: "Функция для перехода к одной мировой итерации. Она проходит через промежуток времени (в секундах), который необходимо продвинуть". - и предположительно время кадра примерно постоянное, поэтому естественно ожидать, что выходной сигнал будет примерно постоянным (а число очень близко к 0).

Вам нужно собрать все дельты и сложить их. Если вас интересует только время с начала симуляции, вам не нужно getCurrentTime в main - просто добавьте дельты. Это означает, что вы должны хранить время в вашем штате. Если вам нужно иметь дело с реальным временем, я предлагаю вам придерживаться UTCTime или другая абстракция, которая проясняет, каким количеством вы манипулируете:

import Graphics.Gloss 
import Graphics.Gloss.Interface.Pure.Game
import Data.Time.Clock

data World = World 
  { startTime 
  , time :: UTCTime 
  } 

-- make explicit what units are being used
seconds2DiffTime :: Float -> NominalDiffTime
seconds2DiffTime = realToFrac 
diffTime2seconds :: NominalDiffTime -> Float 
diffTime2seconds = realToFrac 

displayWorld World{time,startTime} = 
  Rotate rot $ color red $ Polygon[(-10, 100), (10, 100), (0, 125)]
    where rot = diffTime2seconds $ diffUTCTime time startTime 

updateWorld dt w = w {time = addUTCTime (seconds2DiffTime dt) $ time w}

main = do 
  t0 <- getCurrentTime
  let initialState = World { time = t0, startTime = t0 }
  play (InWindow "" (400,400) (400,400)) 
       white
       30 
       intialState 
       displayWorld 
       (const id) 
       updateWorld

Это дает вам как истекшее время с начала симуляции, так и реальное время на часах.

Обратите внимание, что вы не должны размещать код рисования изображения внутри timeHandler - эта функция может вызываться гораздо чаще, чем необходимо для перерисовки изображения, что приводит к большой дополнительной работе. Вместо этого сделайте свой рисунок в displayWorld как указано выше.

Другие вопросы по тегам