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

时间:2011-11-04 15:53:14

标签: javascript html5 canvas coffeescript

由于我的其他错误已经解决,我正在为此错误发布一个新问题。

我制作了一个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

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

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

我不知道如何正确调试..当我console.log时,游戏循环会向我发送消息。

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

提前致谢。

4 个答案:

答案 0 :(得分:7)

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

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

if allowedDirections.indexOf(newDirection) > -1 and !turnedThisFrame
    Snake.direction = newDirection
    turnedThisFrame = true

然后,在每帧运行的代码中,将turnedThisFrame设置为false

第二个是重写你如何处理按键。这通常是我采取的方法。保留按下哪些键的地图(例如,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

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

地图directionToKey只会与您的keysToDirections相反。

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

为第二种方法增加了优势:在菜单屏幕和游戏之间切换时,您不必更改键处理程序回调。你只需要一个不同的tick函数来检查被按下的内容。

答案 1 :(得分:1)

您必须保留蛇实际移动的最后一个方向,并根据该方向确定allowedDirection。按一个键只表示朝那个方向移动的意图,但是当按下键时它实际上不会移动,但是根据游戏的速度,我猜。

答案 2 :(得分:0)

你的蛇吃自己显示你的命中检测(和命中检测处理)代码有问题。如果你正在击中蛇,游戏应该结束。蛇不是苹果!没关系,显然我错过了游戏 为你结束的部分。

如果您选择允许像素粒度(我看不到您的演示,我的工作网络是半截...),您无法让U转“安全”,就像您可以使用十六进制粒度方法。不是说你的选择很糟糕,只是告诉你选择你的战斗,有些你无法获胜。

答案 3 :(得分:0)

我遇到了同样的问题,但我通过实施队列解决了这个问题;每个按键设置蛇的方向,并且(新)方向被推到一个数组。然后,在我实际在游戏循环中移动蛇之前,我检查我的方向队列中是否有任何元素。如果是这样,我通过Array.shift将其移动一次。返回值是队列的第一个元素和我的蛇的新方向。 Array.shift还会从队列中删除该元素,这正是我们想要的。如果几乎同时按下两个键,则第一个和第二个方向改变存储在我们的队列中,我们上面提到的例程通过首先在下一个可用的滴答中应用第一个方向改变然后应用第二个方向改变来处理其余的在此后的下一个刻度。

希望有道理吗? : - )