Долгое время я хотел, чтобы все двигалось в браузере. Проблема, с которой я сталкивался долгое время, - это попытки разобраться в синхронном коде и асинхронном коде. Как я могу заставить игрока двигаться, когда я нажимаю клавишу, но смешиваю основанную на событиях природу браузера с синхронным стилем кодирования, который я использую при решении проблем или головоломок.

Сегодня мы создадим простого персонажа, который может перемещаться и прыгать в пространстве. Мы добьемся этого с помощью ~ 70 строк JavaScript без помощи каких-либо библиотек. Следуйте инструкциям с помощью CodePen или вашего собственного редактора.

Я предполагаю, что у вас есть базовое понимание синтаксиса JavaScript, включая некоторые базовые функции ES6. Если не читайте сначала мое освежение по ES6!

Щелкните здесь, чтобы увидеть готовый код.

Чтобы создать простой код, которым можно было бы управлять, мы разделим отображение игры с игровыми данными. Данные игры будут находиться в объекте, который будет отрисован с помощью функции update.

Сначала давайте настроим html, чтобы мы могли видеть, что мы делаем. Мы будем использовать элементы svg, потому что их легко настроить и они достаточно быстрые для наших нужд. Мы будем использовать прямоугольник для нашего плеера и линию, чтобы установить границу пола. Чтобы настроить экран svg с линией и прямоугольником, добавьте следующий код в свой html.

Это нарисует экран svg. Необходимо указать ширину и высоту, но мы сделаем это с помощью JavaScript. Линия пока не будет видна, так как она выходит за пределы экрана svg.

Давайте настроим данные для экрана и плеера на JavaScript.

Экран теперь будет включать землю, нарисованную в нижней части экрана. Элементы SVG можно изменять с помощью CSS так же, как стили для других элементов. Однако элементы svg имеют разные свойства CSS, специфичные для фигур svg.

Прежде чем перейти к логике, давайте взглянем издалека на процесс, который мы будем использовать. Нам нужно, чтобы функция запускалась ~ 30 раз в секунду для изменения данных в игре, а также для рисования экрана. Мы хотим, чтобы такие события, как нажатие и отпускание клавиши, отслеживались и, следовательно, влияли на выполняемую логику. Мы хотим хранить данные в каком-то хранилище данных (объект, который мы назовем worldData), а затем строить наш игровой цикл на этих данных. В конце нашего игрового цикла также будет update функция, создающая фрейм.

Итак, давайте настроим данные и функцию обновления!

Для наших игровых данных требуются координаты x и y для игрока. Мы сделаем это объектом, чтобы мы могли легко добавлять данные позже. Я хочу, чтобы игрок начинал с земли по центру экрана, но не стесняйтесь писать свои числа.

Однако прямоугольник плеера еще не сдвинулся. Чтобы переместить его, нам нужно создать функцию update или render. Называйте это как угодно, но я буду использовать update. Эта функция использует worldData для рисования кадра. Это не должно быть слишком сложно, поскольку все, что мы будем делать, - это связывать worldData.player.x со свойством прямоугольников x и то же самое со свойством y.

Теперь прямоугольник переместится в положение, указанное вами в worldData объекте.

Давайте создадим игровой цикл, который выполняется 30 раз в секунду. В этом цикле будет вся наша логика, и он закончится вызовом функции update(), которая обновляет svg, чтобы отразить новые данные.

Теперь ваш игрок отрывается от земли! Давайте разберемся, почему это происходит. setInterval принимает функцию и вызывает ее снова и снова с интервалом в миллисекундах. В этом случае мы вызываем функцию каждые 30 миллисекунд. Это примерно 33 кадра в секунду. Каждый раз, когда эта функция запускается, она изменяет worldData.player.y путем вычитания 0.5. В конце функции мы обновляем фрейм, вызывая update. Без update мы не увидим взлета!

Давайте добавим взаимодействие. Нам нужен способ узнать, какая клавиша не работает. Это самая сложная часть, но как только вы ее поймете, вы сможете добавить в игру столько ключей, сколько захотите, и это легко. Нам нужно отслеживать, какие клавиши не работают. Мы добавим keysDown в качестве свойства к объекту worldData. keysDown будет содержать список всех клавиш, которые удерживает игрок, играющий в вашу игру. С помощью этой информации мы сможем увидеть, какие клавиши не работают в игровом цикле (функция в setInterval выше), и соответствующим образом переместить игрока.

Сначала давайте добавим keysDown как свойство в объекте worldData. Теперь это должно выглядеть так:

Мы используем прослушиватели событий документа, чтобы отслеживать нажатые клавиши.

На консоли вы можете видеть, как события запускаются при нажатии кнопок на клавиатуре. Давайте создадим функцию, которая добавляет нажатые клавиши со стрелками в список в вашем worldData объекте. Мы вызовем эту функцию fireKeyAction, и она будет обрабатывать события keydown и keyup.

Мы также хотим обновить наши события нажатия и нажатия клавиш.

Давайте быстро пройдемся по нему.

Когда вы нажимаете клавишу, он находит имя нажатой клавиши с помощью event.key. Это строковое представление ключа, например ArrowLeft. Это передается в fireKeyAction, который принимает два аргумента. Строка ключа и логическое значение, указывающее, является ли это событием keydown или keyup.

Предполагая, что мы обрабатываем keydown событие. В fireKeyAction мы передаем аргумент isDown как true. Затем мы достигаем оператора switch. Это сокращенный метод проверки, соответствует ли key === различным case операторам. В этом примере мы будем делать что-либо, только если key соответствует любой из клавиш со стрелками.

Если пользователь нажал или отпустил клавишу со стрелкой, мы добавляем или удаляем ее из массива keysDown. Если isDown равно true, мы добавляем клавишу со стрелкой, но только если ее еще нет в массиве. Это важно, потому что событие нажатия клавиши срабатывает постоянно, пока клавиша нажата. Мы хотим добавить ключ только один раз. Если isDown равно false, мы находим ключ с indexOf и используем splice, чтобы вырезать его из массива keysDown.

Вернитесь к главному циклу (функция внутри setInterval) и добавьте console.log для worldData.keysDown.

Осталось лишь добавить логики в игровой цикл. Заставим персонажа ходить влево и вправо. Обновите анонимную функцию setInterval, чтобы она соответствовала следующему:

Обратите внимание, что я использовал функцию isKeyDown в if операторах. Давайте быстро добавим эту функцию:

Это просто ярлык для проверки наличия ключа в массиве.

Добавьте другие функции, добавив ключ, дополнительную логику в игровой цикл setInterval или добавив дополнительные элементы svg.

На то, как я организовал код, сильно повлияла книга Как разрабатывать программы, второе издание, которая находится в свободном доступе в Интернете!

В этом уроке используется мутация объекта worldData, что не является хорошей практикой, особенно когда все становится сложно. Если вы хотите усложнить и развить свою игру, я бы порекомендовал взглянуть на Redux. Проверьте это введение в Redux, сделав еще одну игру! Redux дает вам безопасный способ изменить состояние вашей игры и отслеживать изменения. В качестве мотивации с Redux вы можете легко добавить в свою игру путешествия во времени.

Есть вопросы, комментарии или обнаруженные ошибки? Напиши мне в Твиттере в моих твиттерах! :)

Хакерский полдень - это то, с чего хакеры начинают свои дни. Мы часть семьи @AMI. Сейчас мы принимаем заявки и рады обсуждать рекламные и спонсорские возможности.

Чтобы узнать больше, прочтите нашу страницу о нас, поставьте лайк / напишите нам в Facebook или просто tweet / DM @HackerNoon.

Если вам понравился этот рассказ, мы рекомендуем прочитать наши Последние технические истории и Современные технические истории. До следующего раза не воспринимайте реалии мира как должное!