Canvas Game: моя змея ест сама - PullRequest
7 голосов
/ 04 ноября 2011

Так как моя другая ошибка была решена, я публикую новый вопрос для этой ошибки.

Я сделал игру Snake canvas, но моя змея, как правило, ест себя, когда вы нажимаете две кнопки одновременно ... Я не уверен, как это правильно объяснить, но вот что происходит:

Скажите, что моя змея движется влево, и я нажимаю + вправо, она съест себя и запустит игру. То же самое, когда он идет направо: вниз + влево и бац, мертвый. Кажется, я не могу воспроизвести ошибку, когда змея поднимается и опускается ..

Это код, связанный с изменением направления:

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 в файл сценария кофе, и это ничего не изменило. Я полностью потерян здесь, поэтому я надеюсь, что кто-то сможет определить, где я иду не так.

Кажется, что все события нажатия клавиш правильные, но они переопределяются, когда вы нажимаете слишком быстро. Если это имеет смысл? Как ... Вы нажимаете вверх, когда змея идет направо, но затем вы нажимаете влево, прежде чем у нее действительно появится шанс подняться, а затем она просто идет налево. Хаотическое предложение прямо здесь, я предлагаю вам немного поиграть и попытаться воспроизвести это, если вы так же заинтригованы, как и я: (

Я понятия не имею, как правильно отлаживать это ... Цикл игры имеет тенденцию спамить меня сообщениями, когда я делаю console.log.

Демонстрационную версию и ссылку на мой репозиторий Github можно найти здесь

Заранее спасибо.

Ответы [ 4 ]

7 голосов
/ 04 ноября 2011

Проблема в том, что, если вы нажмете клавиши достаточно быстро, возможно вызвать многократный обратный вызов события в течение одного кадра.Таким образом, если змея идет вниз, она может повернуть направо, а затем вверх в том же кадре, изменяя направление и съедая себя.Я предложу два способа решения этой проблемы:

Первый - установить флаг при изменении направления, например:

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

, а затем в вашем коде, который запускается каждыйframe, установите turnedThisFrame на false.

Второе - это переделать то, как вы справляетесь с нажатиями клавиш.Это часто подход, который я использую.Сохраните карту, на которой нажаты клавиши (скажем, keysDown), и свяжите функцию, которая устанавливает keysDown[e.which] = true на нажатие клавиши, и другую функцию, которая устанавливает keysDown[e.which] = false на нажатие клавиши.Затем вы можете проверить, какие клавиши нажимаются в коде, выполняющем каждый кадр, и действовать соответствующим образом.

Вот некоторые подробности о том, как я реализовал второе решение в текущем проекте.Следующий код появляется в моем обработчике загрузки:

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 голос
/ 04 ноября 2011

Вы должны сохранить последнее направление, в котором фактически перемещалась змея, и определить разрешенное направление на основании этого.Нажатие клавиши представляет собой только намерение двигаться в этом направлении, но фактически оно движется не при нажатии клавиши, а, скорее всего, исходя из скорости игры.

0 голосов
/ 20 февраля 2018

У меня была такая же проблема, но я решил ее, реализовав очередь;каждое нажатие клавиши устанавливает направление змеи, и это (новое) направление помещается в массив.Затем, прежде чем я действительно перемещу змею в игровом цикле, я проверяю, есть ли какие-либо элементы в моей очереди.Если это так, я сдвигаю его один раз через Array.shift.Возвращаемое значение - это первый элемент очереди и новое направление для моей змеи.Array.shift также удаляет этот элемент из очереди, и это именно то, что нам нужно.Если две клавиши нажаты почти одновременно, первое и второе изменение направления сохраняются в нашей очереди, и наша вышеупомянутая процедура заботится об остальном, сначала применяя первое изменение направления в следующем доступном тике, а затем применяя второе изменение направленияв следующем тике.

Надеюсь, что имеет смысл?: -)

0 голосов
/ 04 ноября 2011

Ваша змея сама по себе показывает проблему с вашим кодом обнаружения попадания (и обработки обнаружения попадания).Если вы попали в змею, игра должна закончиться.Змея - не яблоко! Неважно, по-видимому, я пропустил ту часть, где игра заканчивается для вас.

Если вы решите разрешить детализацию пикселей (я не могупосмотрите вашу демонстрацию, сеть моей работы наполовину отключена ...), вы не можете сделать U-образные "безопасными", как вы могли бы с помощью подхода с шестигранной гранулярностью.Не сказать, что ваш выбор плох, просто сказать вам, чтобы выбрать ваши битвы, некоторые из которых вы просто не можете выиграть.

...