【问题标题】:Haskell Gloss - Do something every frame key is pressedHaskell Gloss - 每按下帧键就做一些事情
【发布时间】:2019-03-23 02:59:00
【问题描述】:

我想在 Haskell Gloss 中每按下一个键的帧移动一个对象,而不仅仅是开始按下键的那一帧。 (示例:按下“w”键时,每帧加速对象)

编辑:我尝试使用 EventKey 的第二个参数但无济于事。

我的代码:

--TODO - Holding keys doesn't work yet
handleKeys :: Event -> AsteroidsGame -> AsteroidsGame
handleKeys (EventKey (Char char) _ _ _) game 
        | char == 'w' = move   0   1
        | char == 'a' = move (-1)  0
        | char == 's' = move   0 (-1)
        | char == 'd' = move   1   0
    where move x y = game {player = accelerateObject (player game) x y}
handleKeys _ game = game

accelerateObject :: Object -> Float -> Float -> Object
accelerateObject obj hor ver = obj {vel = (vx + hor, vy + ver)}
    where (vx, vy) = vel obj

【问题讨论】:

  • 只跟踪 AsteroidGame 中的按键?我想 EventKey 会告诉你键是被按下还是被释放。
  • 正如@Krom 所说,检查Down 事件和Up 事件。根据您的环境,您应该会看到重复的 Down 事件,直到密钥被释放,这应该显示为 Up 事件。
  • @BobDalgleish 我认为 Down 事件仅在按下键时触发,而不是在已经按下时一次又一次地触发。所以它不起作用。我不知道如何确认没有更多事件,即我不知道如何在 Haskell 的其他代码中间打印一些调试信息
  • 您可以使用Debug.Trace 包插入trace 语句。通常,事件系统应在键开始自动重复时发出事件。例如,任何文本编辑器都会在您按住字符键时记录每个字符。检查文档以查看是否有某些内容没有过滤自动重复字符。
  • @BobDalgleish 我现在可以确认按下按钮时只有一个事件,按下按钮时没有更多事件,所以我仍然没有解决方案。

标签: haskell input gloss


【解决方案1】:

正如 OP 正确推断的那样,gloss 为您提供输入事件(“刚刚按下键”、“刚刚移动鼠标”),而不是输入状态(“当前按下键”、“鼠标位于 x,y”) .似乎没有内置的方式来查看每一帧的输入状态,所以我们必须自己解决。谢天谢地,这并不太难!

对于一个简单的工作示例,我们将制作一个非常有趣的“游戏”,您可以在其中观看计数器在按下空格键时向上计数。铆。这种方法适用于处理任何按键,因此很容易扩展到您的情况。

我们首先需要的是我们的游戏状态:

import qualified Data.Set as S

data World = World
    { keys :: S.Set Key
    , counter :: Int }

我们会跟踪我们的特定游戏状态(在本例中只是一个计数器),以及我们解决方法的状态(一组按下的键)。

处理输入事件只涉及向我们当前按下的键集添加一个键或删除它:

handleInput :: Event -> World -> World
handleInput (EventKey k Down _ _) world = world { keys = S.insert k (keys world)}
handleInput (EventKey k Up _ _) world = world { keys = S.delete k (keys world)}
handleInput _ world = world -- Ignore non-keypresses for simplicity

这可以很容易地扩展到处理例如。鼠标移动,通过更改我们的 World 类型来跟踪光标的最后已知坐标,并在我们看到 EventMotion 事件时将其设置在此函数中。

我们的逐帧世界更新函数然后使用输入状态来更新特定的游戏状态:

update :: Float -> World -> World
update _ world
    | S.member (SpecialKey KeySpace) (keys world) = world { counter = 1 + counter world }
    | otherwise = world { counter = 0 }

如果当前按下了空格键 (S.member (SpecialKey KeySpace) (keys world)),则递增计数器 - 否则,将其重置为 0。我们不关心帧之间经过了多少时间,因此我们忽略了 float 参数。

最后我们可以渲染我们的游戏并玩它:

render :: World -> Picture
render = color white . text . show . counter

main :: IO ()
main = play display black 30 initWorld render handleInput update
    where
        display = InWindow "test" (800, 600) (0, 0)
        initWorld = World S.empty 0

【讨论】:

    猜你喜欢
    • 2012-05-19
    • 1970-01-01
    • 2018-06-18
    • 1970-01-01
    • 2015-10-20
    • 2013-11-04
    • 1970-01-01
    • 2015-07-17
    • 1970-01-01
    相关资源
    最近更新 更多