画布游戏:我的蛇吃了自己

cab*_*ret 8 javascript html5 canvas coffeescript

由于我的其他bug已经解决,我正在为这个bug发布一个新问题.

我制作了一个Snake帆布游戏,但是当你同时按下两个按钮时,我的蛇往往会吃掉它自己.我不知道如何正确解释它,但这就是发生的事情:

说我的蛇向左移动,我向下按+向右,它会吃掉它自己并引发游戏结束.当它向右移动时:向下+向左和向下,死亡.当蛇上下来时,我似乎无法重现这个错误.

这是改变方向所涉及的代码:

bindEvents = ->
    keysToDirections =
        37 : LEFT
        38 : UP
        39 : RIGHT
        40 : DOWN

    $(document).keydown (e) -> 
        key = e.which
        newDirection = keysToDirections[key]

        if newDirection
            setDirection newDirection
            e.preventDefault()

setDirection = (newDirection) ->
    # TODO: there's some bug here; try pressing two buttons at the same time..
    switch Snake.direction
        when UP, DOWN
            allowedDirections = [LEFT, RIGHT]
        when LEFT, RIGHT
            allowedDirections = [UP, DOWN]

    if allowedDirections.indexOf(newDirection) > -1
        Snake.direction = newDirection
Run Code Online (Sandbox Code Playgroud)

我认为编译的JS有问题,因为我的switch语句没有break最后一个case; 但是,我尝试添加else return到咖啡脚本文件,这没有改变任何东西.我完全迷失在这里,所以我希望有人能够找到我出错的地方.

好像它需要keydown事件正确,但是当你按得太快时它们会被覆盖.如果这有道理?就像..当蛇向右走的时候你会按下,但是在它实际上有机会上升然后它向左移动之前你向左按.混乱的句子就在那里,我建议你玩一会儿,并尝试重现这个,如果你像我一样好奇:(

我不知道如何正确地调试它.当我这样做时,游戏循环往往会向我发送消息console.log.

可以在这里找到现场演示和我的github回购链接

提前致谢.

Aar*_*our 8

问题是如果你足够快地按下键,它可能在一帧期间多次触发事件回调.因此,如果蛇正在下降,它可以向右转,然后在相同的框架中向上,从而逆转方向和吃自己.我会建议两种方法来解决这个问题:

第一种是在方向改变时设置标志,即:

if allowedDirections.indexOf(newDirection) > -1 and !turnedThisFrame
    Snake.direction = newDirection
    turnedThisFrame = true
Run Code Online (Sandbox Code Playgroud)

然后,在每帧运行的代码中,设置turnedThisFramefalse.

第二是重写你如何处理按键.这通常是我采取的方法.保留按下哪些键的地图(例如keysDown),并绑定一个设置keysDown[e.which] = true为keydown的函数和另一个设置keysDown[e.which] = false为keyup的函数.然后,您可以检查在每个帧运行的代码中按下了哪些键,并采取相应措施.

以下是我在当前项目中如何实现第二个解决方案的一些细节.以下代码出现在我的onload处理程序中:

do ->
  keysDown = []

  $(document).keydown (e) ->
    keysDown.push e.which

  $(document).keyup (e) ->
    keysDown = _.without keysDown, e.which

  window.isPressed = (keyCode) -> keyCode in keysDown
Run Code Online (Sandbox Code Playgroud)

do ->构造用于创建一个函数并立即调用它,这具有保持keysDown处理程序关闭的有益效果isPressed,同时避免污染onload回调的主要范围.

然后,在我的tick函数的开头(每帧运行一次并处理游戏逻辑,绘制屏幕等)我会有这样的事情:

switch Snake.direction
    when UP, DOWN
        allowedDirections = [LEFT, RIGHT]
    when LEFT, RIGHT
        allowedDirections = [UP, DOWN]

for direction in allowedDirections
    if isPressed(directionToKey[direction])
        Snake.direction = newDirection
        break
Run Code Online (Sandbox Code Playgroud)

地图directionToKey只是你的对面keysToDirections.

请注意,这意味着首先列出的键allowedDirections将具有优先权,即如果您向右并在同一帧中向上和向下按下,则无论先按哪个键都会出现向上.

增加了第二种方法的优势:在菜单屏幕和游戏之间切换时,您不必更改键处理程序回调.你只需要一个不同的tick功能来检查按下的是什么.