3

我想在按下键的每一帧移动 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
4

1 回答 1

4

正如 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
于 2018-12-06T11:49:24.997 回答