Приветствую всех, кто читает данную статью и загорелся желанием написать свою собственную игру. Сегодня я расскажу вам, как создается игра змейка на JavaScript. Я приведу код для html-разметки, после мы проработаем с вами стили оформления элементов и в конце пропишем логику работы приложения при помощи JavaScript. Давайте приступим!
Создаем свою игру «Змейка»
Займемся разметкой игрового поля
Для начала необходимо прописать разметку игры. В первую очередь на странице выделим отдельное пространство под рабочую область:
<div class="border"></div>
Я хочу, чтобы в моем приложении было несколько уровней сложности: легкий, средний и сложный. Также я хочу обеспечить пользователя возможностью сбросить игру и выбранный уровень сложности для того, чтобы он смог начать игру заново. Поэтому я создаю четыре кнопки, которые помещаю в отдельный блок buttons.
1 2 3 4 5 6 | <div class="buttons"> <button id="easy" onclick="EasyStart()">Простой</button> <button id="medium" onclick="MediumStart()">Средний</button> <button id="hard" onclick="HardStart()">Сложный</button> <button onclick="reload()">Сбросить</button> </div> |
Как вы уже заметили, на каждую кнопку я навешиваю событие. Поэтому при нажатии на какую-либо из них будет запускаться определенная функция обработки.
Ну и в конце я хочу, чтобы у меня выводился счет.
<div class = «score»>Ваш счет: 0</div>
Зададим правильное отображение элементов
Пока что ничего симпатичного в редакторах кода не отображается. Для этого стоит задать стилевые правила. Если вы пишете программу полотном, то внутри тега <head> пропишите парный элемент <style> </style> и далее в него вносите css.
Или же создайте файл style.css и без указания style вставляйте описание стилей.
Итак, для начала сбросим стандартные параметры браузера, т.е. обнулим внутренние и внешние отступы:
body{
margin: 0;
padding: 0;
border: 0;
}
Теперь создадим подложку под игровое поле:
1 2 3 4 5 6 7 8 9 10 | .border{ z-index: -15; position: absolute; top: 9px; background-color: #556B2F; left: 39px; width: 510px; height: 559px; border: 2px solid #001100; } |
Перейдем к кнопкам. Я хочу сделать их объемными и разместить сверху по центру поля. Поэтому для соответствующего класса я описываю внешние отступы. А после для самого элемента <button> определяю стилевые характеристики.
1 2 3 4 5 6 7 8 9 | button { border-radius: 10px; padding: 7px; background: linear-gradient(to top, #FFFAFA, #8B8682); font-weight: 600; } .buttons{ margin: 20px 10px 20px 140px; } |
Что-то уже прорисовывается, но описание счета «убежало» непонятно куда. Поэтому переместим его в нужную нам позицию и облагородим.
1 2 3 4 5 6 | .score{ margin-left: 72px; font-size: 31px; color: #FF0000; text-shadow: 0 0 6px #010; } |
Вот мы и описали классы и теги, которые были перечислены в гипертекстовой разметке. Однако у вас наверняка возникли вопросы: «А где же само игровое поле? Где змея и что она должна собирать?»
Остальная разметка будет добавляться в скрипте. А пока подготовим для этого стили.
Само игровое поле мы опишем при помощи класса .field, а внешний вид клеток – с помощью класса .cell.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | .field{ z-index: 30; height: 23px; margin-left: 53px; } .cell{ margin: 0; display: inline-block; background-color: #7FFFD4; width: 22px; height: 22px; border: 1px solid #010; border-radius: 4px; z-index: 30; transition: background-color 0.3s; } |
Далее нам нужна змея. Поэтому задаем ее классом .snake и указываем:
1 2 3 4 5 6 | .snake{ box-shadow: 0 0 10px #000000 inset; background: linear-gradient(to right, #00FF00, #556B2F); z-index: 30; transition: background 0.2s; } |
Ну и наконец змея должна что-то собирать. Поэтому описываем еду. В моей программе это будет квадрат красного цвета.
1 2 3 4 5 6 | .food{ background-color:#FF0000; z-index: 30; box-shadow: 0 0 10px #000000 inset; transition: background-color 2s; } |
Дизайн игры готов. Теперь переходим к самому главному!
Заставим игру работать!
Вот теперь пришло время прописать логику работы «Змейки». Для этого после разметки html вставьте ниже прикрепленный код. Внимательно просмотрите его, если хотите разобраться во всех деталях реализации игры. По мере прочтения коды вы будете находить мои комментарии.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | <script type="text/javascript"> var timer; var directx = direct = 0; //Указываю количество клеток по вертикали и после по горизонтали var fieldSizeX = 20; var fieldSizeY = 20; var KEY = { 'left' : 37, 'up' : 38, 'right' : 39, 'down' : 40 }; //Массив направлений var direction = [ [0,1], //вправо [1,0], //вниз [0,-1], //влево [-1,0]]; //вверх //Задаю начальные параметры змеи var snake = { length : 3, body : [[1,1],[1,2],[1,3]], //координаты расположения змеи /* В этой функции рисую змею на экране*/ initialisationSnake : function (){ //Цикл проходит по всем составным частям змеи for ( var i = 0; i < this.length; i++){ var currentBodyPart = this.body[i]; /*Отрисовка происходит при помощи присвоения определенных стилевых правил cell и snake */ document.getElementById(currentBodyPart.join()).className = 'cell snake'; } }, move : function (){ direct = directx; var body = this.body //определяем тело ползучего животного //Определяем его голову. В данном случае это последняя клетка var head = this.body[this.length-1]; var headCell = head.map(function(value, index){ return value + direction[direct][index] }); compareEatOrGameOver(headCell, body); return headCell; } }; //функция addEventListener регистрирует определенный обработчик событий //и keyHandler сообщает какая кнопка была нажата window.addEventListener('keydown', keyHandler, false); prepareGamePane(fieldSizeX, fieldSizeY); /*Подготавливаю игровое поле. Именно здесь прорисовываются клетки поля и расположение последнего */ function prepareGamePane (fieldSizeX, fieldSizeY){ for ( var x = 0; x < fieldSizeX; x++){ var coordinateX = document.createElement('div'); document.body.appendChild(coordinateX); coordinateX.className = 'field'; for (var y = 0; y < fieldSizeY; y++){ var coordinateY = document.createElement('div'); coordinateX.appendChild(coordinateY); coordinateY.className = 'cell'; coordinateY.id = x+','+y; } } snake.initialisationSnake(); makeFood(fieldSizeX, fieldSizeY); } //Генерирую случайную точку на поле, в которую и внесу еду function makeFood (fieldSizeX, fieldSizeY){ //Случайным образом получаю первую часть координаты по x var x = Math.round(Math.random() * (fieldSizeX-1)); //Случайным образом получаю первую часть координаты по y var y = Math.round(Math.random() * (fieldSizeY-1)); var food = document.getElementById(x+','+y); /*Осуществляю проверку. Если полученная координата не занята змеей, то рисую еду – красный квадрат*/ if (food.className == 'cell'){ food.className = "cell food"; } else { //иначе запускаю функцию по новой makeFood(fieldSizeX, fieldSizeY); } return food; } //Обработчик нажатой кнопки function keyHandler (event){ switch (event.keyCode) { case KEY.left: //стрелка влево if (direct != 0){ directx = 2; } break; case KEY.right: //стрелка вправо if (direct != 2){ directx = 0; } break; case KEY.up: //стрелка вверх if (direct != 1){ directx = 3; } break; case KEY.down: //стрелка вниз if (direct != 3){ directx = 1; } break; default : return; } } function compareEatOrGameOver (headCell, body) { var tmp = document.getElementById(headCell.join()); /*Данная проверка позволяет не останавливаться ползучему существу, если оно доходит до края игрового поля*/ if (tmp == null ) { if (headCell[0] == -1) headCell[0] = fieldSizeX - 1; if (headCell[0] == fieldSizeX) headCell[0] = 0; if (headCell[1] == -1) headCell[1] = fieldSizeY - 1; if (headCell[1] == fieldSizeY) headCell[1] = 0; tmp = document.getElementById(headCell.join()); } //Если занята ячейка – это пустая клетка, то рисую там змею if ( tmp != null && tmp.className == 'cell' ){ var removeTail = body.shift(); body.push(headCell); document.getElementById(removeTail.join()).className = 'cell'; document.getElementById(headCell.join()).className = 'cell snake'; } else { //если текущая клетка с едой if ( tmp != null && tmp.className == 'cell food'){ //увеличиваю длину змеи на 1 квадрат snake.length++; body.push(headCell); document.getElementById(headCell.join()).className = 'cell snake'; //генерирую еду в другой ячейке makeFood(fieldSizeX, fieldSizeY); //Меняю количество очков var score = snake.length-3; document.getElementById('score').innerHTML = 'Ваш счет: '+score; } else { //если текущая ячейка оказалась самой змеей if (tmp.className == 'cell snake'){ clearInterval(timer); //вывод сообщения об окончании игры alert('Вы проиграли! Ваш счет: ' + (snake.length-3) + '. Нажмите кнопку «Сбросить» для начала новой игры!'); } } } } //Функции описания режима движения змейки function EasyStart () { //Простой timer = setInterval(function(){ snake.move(); },400); } function MediumStart () {//средний timer = setInterval(function(){ snake.move(); },200); } function HardStart () {//сложный timer = setInterval(function(){ snake.move(); },100); } function reload () {//перегрузка параметров (кнопка «Сбросить») window.location.reload(); } </script> |
Ну вот игра готова!
Вступайте в ряды моих верных подписчиков и делитесь интересными статьями с друзьями. Приятной игры! Пока-пока!
С уважением, Роман Чуешов
Руслан, как и чем код компилировать? Где-то статья есть об этом? У меня не получается.
Открой html-файл через браузер.
Спасибо! Моя ошибка была в создании файла с расширением js, а надо было html. Невнимательно прочитал в статье, что код вставляется после разметки html. Мой косяк.
Игра, как раз для начинающих, относительна проста и с подробными комментариями автора. На её основе можно написать свою игру. Я по крайней мере попробую.
Ну вы бы хотя бы ссылку на фиддл оставили, чтобы поиграть )
Не могу разобраться в этой части кодаё//Определяем его голову. В данном случае это последняя клетка
var head = this.body[this.length-1];
var headCell = head.map (function (value, index){ return value + direction[direct][index] });
compareEatOrGameOver (headCell, body);
return headCell; }
Value и index изначально не заданы, потом принимают значения 1 и 0, далее в этой строке принимают значение 4 и 1 и в новом цикле 1 и 0 потом 5 и 1 (если мы не нажимаем стрелки) и т.д. как значения изменяются что-то не пойму. Похоже на рекурсию.
Может кто нибудь объяснить 29-ую строчку в js.
document.getElementById (currentBodyPart.join ())............
Метод getElementById позволяет получить элемент (тег) страницы по его атрибуту id. Какое в данном случае id и какого элемента в HTML файле? Я так понимаю currentBodyPart.join () это просто строка из элемента массива в 22-ой строке. В первой итерации цикла она будет ровна «1,1». Но это же точно никакое не id
Спасибо
Пожалуйста!
Подскажите, а как сделать, чтобы работал счет?
у вас тут ошибка вместо
Ваш счет: 0
надо
Ваш счет: 0
там где кусок кода с выводом счета в html
надо
class = 'score'
а не
class = «score»