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
как указано выше.