【问题标题】:Haskell Gloss: time variable stays constantHaskell Gloss:时间变量保持不变
【发布时间】:2016-10-22 18:26:07
【问题描述】:

我正在尝试在 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

三角形 (=player) 存储在“世界”数据类型中: 模块型号在哪里

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

它目前只返回playerPicture。

现在,在我的 timeHandler 模块中,我想使用时间(在main 函数中提供给 timeHandler)来旋转player,如下所示:

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

这不起作用。我用一个常量值(在 timeHandler 函数中)替换了time,这确实旋转了图片。所以看起来time 没有更新..我做错了什么?

【问题讨论】:

  • 不是一个答案:有一点要明确的是,main 中的 timetimeHandler 中的 time 彼此无关——事实上,它们甚至没有相同的类型。前者是执行getPosixTime得到的局部值,后者是独立函数定义中的参数。他们只是碰巧同名。
  • 我知道这个@duplode!但是,根据play函数的描述,timeHandler应该以秒为单位给出时间..
  • 好吧——只是你在“main 函数中给 timeHandler”的措辞让我有点困惑。 (您不会自己将time 提供给`timeHandler`;相反,Gloss 会在play 实现中的某个地方自行获取它。)
  • 无论如何,如果我正确理解the docs,您的程序的预期行为将是每秒将图片旋转一度(即每帧1/60度),对吗?
  • 没错,这就是我所期望的。

标签: haskell time gloss


【解决方案1】:

当然它不起作用,timeHandler 收到一个数字,实际上,该数字接近自上一帧以来的时间增量 - docs 说:“一个迭代世界的函数。它是通过了需要提前的时间段(以秒为单位)。” - 帧时间大概是恒定的,所以自然会期望输出是恒定的(并且是一个非常接近于 0 的数字)。

您需要收集所有增量并将它们相加。如果您只关心模拟开始以来的时间,那么您不需要main 中的getCurrentTime - 只需添加增量即可。这意味着您必须将时间存储在您所在的州。如果您确实需要处理实时,我建议您坚持使用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 中绘图。

【讨论】:

  • 感谢您的回答!唯一让我有点困惑的是:根据play 的文档,updateWorld 应该以秒为单位接收时间。在您的代码中,您使用time &lt;- getCurrentTime 创建自己的时间变量。这是如何传递给updateWorld的?
  • 你从哪里得到这个想法的?我链接到文档并复制粘贴了相关位 - 阶跃函数 获得自模拟开始或任何其他时间以来的时间(以秒为单位),但模拟应该的时间量被步进(即时间增量)。在任何情况下,main 中声明的初始startTimetime 都处于初始状态-play ... World{..} ...-相当于World{field1 = field1, field2 = field2, &lt;etc&gt;}-这是RecordWildCards
  • RecordWildCards 如果您不熟悉它确实会令人困惑,并且与问题无关,所以我摆脱了它并使用常规记录语法。
  • 这是有道理的。用 Haskell 之类的语言记录状态下的时间是合乎逻辑的。谢谢!
猜你喜欢
  • 2016-04-01
  • 1970-01-01
  • 2020-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-09
相关资源
最近更新 更多