Не так давно я имел опыт в написании игр на JavaScript. Довольно увлекательный процесс. Простая игра демонстрирует некоторый приём, который я вычитал в книге Makzan – “HTML5 Games Development by Example Beginner’s Guide”. Приём связан с неким игровым “циклом”, который раз, например, в 50 миллисекунд перерисовывает отображение в соответствии с изменениями, которые произошли в игре. Использовался jQuery и HTML.
Инициализация
Игровой процесс начинается как правило с инициализации.
Во время инициализации рисуется игровое поле, а точнее – серая рамочка по периметру окна браузера. Это граница поля, за которую нельзя заступать. Это делает функция prepareGamePane()
.
Далее функция initStartPosition()
размещает на игровом поле змейку в соответствии с начальными координатами.
На следующем шаге с помощью функции addRandomRabbit()
на игровом поле размещается так называемый “кролик” (квадратик, который нужно собрать).
Для того, чтобы была возможность управлять змейкой нужно определить обработчик событий нажатия кнопок, а именно – стрелок:
// Обработчик события snake.keyPress = function( e ) { switch(e.which) { case KEY.LEFT: snake.direction = snake.direction != snake.directions.RIGHT ? snake.directions.LEFT : snake.direction; break; case KEY.RIGHT: snake.direction = snake.direction != snake.directions.LEFT ? snake.directions.RIGHT : snake.direction; break; case KEY.UP: snake.direction = snake.direction != snake.directions.DOWN ? snake.directions.UP : snake.direction; break; case KEY.DOWN: snake.direction = snake.direction != snake.directions.UP ? snake.directions.DOWN : snake.direction; break; default:break; } return false; };
Кроме того, нужно определять какая именно стрелка была нажата. Для это заведён такой объект с данными о кодах кнопок:
var KEY = { 'LEFT':37, 'RIGHT':39, 'UP':38, 'DOWN':40 };
В конце инициализации начинаем “игровой цикл” с помощью setInterval()
.
Таким образом, инициализация сводится к вызову всех этих функций один раз после загрузки страницы:
snake.init = function() { this.prepareGamePane(); this.initStartPosition(); this.addRandomRabbit(); $(document).keydown(this.keyPress); this.gameLoop( this ); this.game_interval = setInterval( this.gameLoop, this.speed ); };
Игровой цикл
Итак, игровой цикл у меня состоит из 2 функций, а скорость цикла – 400 миллисекунд. Функция snake.drawSnake()
рисует змею на поле по текущим данным о её местоположении, а функция snake.moveSnake()
соответственно – меняет эти данные в зависимости от направления движения змеи.
То есть 2 функции выполняются раз в 400 миллисекунд:
snake.gameLoop = function() { snake.drawSnake(); snake.moveSnake(); };
Змея перерисовывается на каждом шаге цикла предварительно удаляя предыдущую змею с игрового поля:
snake.drawSnake = function() { $( '.snake', this.pane.instance ).remove(); $.each( this.snake, function( k, v ) { snake.drawSnakeBodySection( k, v ); }); };
Тело змеи представляет собой массив координат в сетке, которая ограничена границами игрового поля. Поэтому, для того, чтобы подвинуть змею на одну клетку на шаге цикла нужно просто добавить в начало массива элемент с новыми координатами “головы” змеи, и удалить один элемент с конца массива. Новые координаты “головы” рассчитываются в зависимости от направления движения змейки.
snake.moveSnake = function() { if ( !this.hitTest() ) { if ( !this.increase_this_step ) { this.snake.pop(); } this.increase_this_step = false; var left = snake.direction[0] == 'h' ? this.getHead().left+snake.body_section.width*snake.direction[1] : this.getHead().left ; var top = snake.direction[0] == 'v' ? this.getHead().top+snake.body_section.height*snake.direction[1] : this.getHead().top ; this.snake.unshift({ 'left':left, 'top':top }); return true; } else { this.handleHitEvent( this.hitTest() ); return false; } };
В этой функции, также, происходит проверка на столкновения с границами поля и “кроликом”.
Поиграть в готовую игру и ознакомиться с исходным кодом можно тут.