Why does my VB.NET Snake game freeze when I hold a key down?

笑着哭i 提交于 2020-01-06 04:05:13

问题


I'm trying to make the classic Snake game in VB.NET, but if I hold a key (any key) during the game, after a few seconds the game freezes until I release the key. I've tried lots to fix this, but nothing works, maybe because I don't understand the problem.

I'm assuming that when I hold down a key, the Form1_KeyDown function gets called, and when, after a few seconds, the key goes into "I'm being held down" mode, that function is constantly called, so the timers don't get a chance to update. But like I said, I'm probably wrong.

Any help at all would be appreciated, I've been struggling with this for a while. I think this is all the necessary code, please let me know if it isn't.

Code for key down event:

 Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown

    ' Sorts out all the key presses: movement, resetting, pausing

    ' Change direction, unless the player tries to travel backwards into themself
    Select Case e.KeyCode
        Case upKey
            If previousDirection <> "D" Then
                nextDirection = "U"
            End If
        Case leftKey
            If previousDirection <> "R" Then
                nextDirection = "L"
            End If
        Case rightKey
            If previousDirection <> "L" Then
                nextDirection = "R"
            End If
        Case downKey
            If previousDirection <> "U" Then
                nextDirection = "D"
            End If
        Case resetKey
            resetGame()
        Case pauseKey
            paused = Not paused
            If paused Then
                lblPaused.Visible = True
                tmrTime.Stop()
                tmrFruit.Stop()
                tmrMove.Stop()
            Else
                lblPaused.Visible = False
                tmrTime.Start()
                tmrFruit.Start()
                tmrMove.Start()
            End If
    End Select

End Sub

Code for the timer that updates/moves the snake (I'm aware this is really inefficient):

 Private Sub tmrMove_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrMove.Tick

    ' Adds a new head in direction of travel, and removes the tail, giving the illusion of snake movement

    Dim head As Object = bodyParts(bodyParts.Count - 1)
    Dim tail As Object = bodyParts(0)
    Dim newHead As Object

    head.Text = ""

    ' Add new head
    Select Case nextDirection

        Case "R"
            ' If snake goes out of bounds
            If head.Tag(0) + 1 >= numberOfColumns Then
                newHead = grid(0, head.Tag(1))
                If newHead.BackColor = snakeColor Then
                    killSnake()
                End If
            Else
                ' If snake overlaps itself
                If bodyParts.Contains(grid(head.Tag(0) + 1, head.Tag(1))) Then
                    killSnake()
                    Exit Sub
                Else
                    ' If snake is fine
                    newHead = grid(head.Tag(0) + 1, head.Tag(1))
                End If
            End If

            ' If fruit taken
            If newHead.BackColor = fruitColor Then
                eatFruit(newHead, tail)
            End If

        Case "L"
            If head.Tag(0) - 1 < 0 Then
                newHead = grid(numberOfColumns - 1, head.Tag(1))
                If newHead.BackColor = snakeColor Then
                    killSnake()
                End If
            Else
                If bodyParts.Contains(grid(head.Tag(0) - 1, head.Tag(1))) Then
                    killSnake()
                    Exit Sub
                Else
                    newHead = grid(head.Tag(0) - 1, head.Tag(1))
                End If
            End If

            If newHead.BackColor = fruitColor Then
                eatFruit(newHead, tail)
            End If

        Case "U"
            If head.Tag(1) - 1 < 0 Then
                newHead = grid(head.Tag(0), numberOfRows - 1)
                If newHead.BackColor = snakeColor Then
                    killSnake()
                End If
            Else
                If bodyParts.Contains(grid(head.Tag(0), head.Tag(1) - 1)) Then
                    killSnake()
                    Exit Sub
                Else
                    newHead = grid(head.Tag(0), head.Tag(1) - 1)
                End If
            End If

            If newHead.BackColor = fruitColor Then
                eatFruit(newHead, tail)
            End If

        Case "D"
            If head.Tag(1) + 1 >= numberOfRows Then
                newHead = grid(head.Tag(0), 0)
            Else
                If bodyParts.Contains(grid(head.Tag(0), head.Tag(1) + 1)) Then
                    killSnake()
                    Exit Sub
                Else
                    newHead = grid(head.Tag(0), head.Tag(1) + 1)
                End If
            End If

            If newHead.BackColor = fruitColor Then
                eatFruit(newHead, tail)
            End If

        Case Else
            newHead = grid(head.Tag(0), head.Tag(1))

    End Select

    bodyParts.Add(newHead)
    newHead.BackColor = snakeColor
    newHead.Font = headFont
    newHead.Text = headText

    ' Remove tail
    tail.BackColor = gridColor
    bodyParts.RemoveAt(0)

    previousDirection = nextDirection

End Sub

回答1:


I'm assuming that when I hold down a key, the Form1_KeyDown function gets called, and when, after a few seconds, the key goes into "I'm being held down" mode, that function is constantly called, so the timers don't get a chance to update. But like I said, I'm probably wrong.

In fact, you are right.

In Windows you'll get a WM_KEYDOWN message as soon as the key is pressed, and then, after a certain interval, you'll be getting lots of WM_KEYDOWN messages with another certain interval between them.

You can find these intervals if you go to Control Panel - Keyboard.

The easiest way of fixing it is adding a call to DoEvents in the end of the key handler.

Try removing the keydown handler completely. Instead, figure nextDirection in the beginnig of tmrMove_Tick by examining Keyboard.IsKeyDown.

Try removing the keydown handler completely. Instead, figure nextDirection in the beginning of tmrMove_Tick by examining GetAsyncKeyState, which you can declare as follows:

Private Declare Function GetAsyncKeyState Lib "user32" Alias "GetAsyncKeyState" (ByVal vKey As Keys) As Short

Private Shared Function IsKeyDown(ByVal Key As Keys) As Boolean
    Return (GetAsyncKeyState(Key) And &H8000S) = &H8000S
End Function



回答2:


I would recommend trying the keyup event instead. It won't spam like the keypress nor keydown events




回答3:


You are correct with the problem being with the key repeating. I have in the past used a variable to hold the previous keystate and exit the keypressed event if it is the same. I am reseting this with a timer that should get you enough of a delay.

If oldKeyData = e.KeyCode Then
    e.Handled = True
    Exit Sub
End If

oldKeyData = e.KeyCode
tmrKeyReset.Enabled = True

Edit: @SpectralGhosts answer will work if you want to move with individual key presses.



来源:https://stackoverflow.com/questions/8626110/why-does-my-vb-net-snake-game-freeze-when-i-hold-a-key-down

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!