Конечно, рисование линий, прямоугольников, треугольников и полукругов весьма занимательное занятие. Но для приобретения реального опыта была поставлена задача – создать что-то функциональное и простое.
Вот так родилась идея написать собственную игру, всем знакомую змейку.
К сожалению, работа и отдых занимают столько времени, что игра была закончена лишь сейчас.
Пусть это будет как демонстрация бета-версии игры. Может быть, кому-нибудь пригодится мой труд. Возможно, я получу инвайт, а заодно с интересом послушаю, критические или похвальные мнения коллег.
Поехали!
Код написан с применением библиотеки jQuery, ибо так удобнее. Все переменные и функции объявляются уже после события загрузки странички. Перечисляем переменные и некоторым задаём дефолтные настройки, также задаём размеры холста и определяем «тело» змейки. Которое будет многомерным массивом. Идея состоит в том, что тело змейки – набор секторов с площадью 9px на 9px, и начало координат каждого сектора является число кратное 10. 1px «справа» от сектора не будет зарисовываться, для визуального разделения.
// объявление переменных, задание им дефолтных значений var canvas, context, first_x, first_y, rabbit_pos = new Array(), rabbit_on_field = false, start = true, state = false, g_over = false, direction = 'right'; // определение слоя и его размеров canvas = $('#mycanvas').get(0); canvas.width = 310; canvas.height = 310; context = canvas.getContext('2d'); // определение цвета змейки context.fillStyle = "#CE3429"; // определение дефолтных секторов змейки var snake_sectors = [[10, 50], [20, 50], [30, 50], [40, 50]];
Функция вывода рамок игрового поля, которую вызовем сразу после объявления дефолтных настроек.
// создание игрового поля function field(context){ context.strokeStyle = "#546DEA"; context.lineWidth = 1; context.strokeRect(7, 7, 295, 295); }
Функция вывода змейки, в которой вызываются другие необходимые функции.
// вывод змейки function snake(){ if(state){ // удаление "хвоста" при движении // при первом проходе координаты есть undefined context.clearRect(first_x, first_y, 9, 9); // определение "поведения" змейки: начало игры, пауза/старт if(start){ // зырисовывается тело змейки в цикле, и объявляется о завершении запуска игры // для визуализации секторов, будем зарисовывать 9px из 10 for(var i in snake_sectors){ context.fillRect(snake_sectors[i][0], snake_sectors[i][1], 9, 9); } start = false; } else{ // зарисовывать новый сектор змейки - "голову" context.fillRect(snake_sectors.slice(-1)[0][0], snake_sectors.slice(-1)[0][1], 9, 9); } // проверка существования кролика на поле и его вывод if(!rabbit_on_field){ rabbit(); } // присваивание переменным значений положения "головы" и "хвоста" var last_x = snake_sectors.slice(-1)[0][0]; var last_y = snake_sectors.slice(-1)[0][1]; first_x = snake_sectors[0][0]; first_y = snake_sectors[0][1]; // определение поведения змейки при различных направлениях движения if(direction == 'right'){ var next_x = last_x + 10; if(next_x > 290){ // врезание в правое поле, конец игры window.setTimeout(game_over, 700); return false; } snake_sectors.push([next_x, last_y]); } if(direction == 'down'){ var next_y = last_y + 10; if(next_y > 290){ // врезание в нижнее поле, конец игры window.setTimeout(game_over, 700); return false; } snake_sectors.push([last_x, next_y]); // добавление нового элемента массива ("голова" змейки) } if(direction == 'up'){ var next_y = last_y - 10; if(next_y < 10) { // врезание в верхнее поле, конец игры window.setTimeout(game_over, 700); return false; } snake_sectors.push([last_x, next_y]); } if(direction == 'left'){ var next_x = last_x - 10; if(next_x < 10) { // врезание в левое поле, конец игры window.setTimeout(game_over, 700); return false; } snake_sectors.push([next_x, last_y]); } // проверка на совпадение последнего элемента массива с другими элементами // т.е. "врезание" змейки в себя for(var i = 0; i < snake_sectors.length - 1; i++){ if(snake_sectors.slice(-1)[0][0] == snake_sectors[i][0] && snake_sectors.slice(-1)[0][1] == snake_sectors[i][1]){ // конец игры window.setTimeout(game_over, 700); return false; } } // проверка на съедание кролика // и определение дальнейшей судьбы "хвоста" if(!is_catching()) snake_sectors.splice(0, 1); else rabbit(); // таймер перезапуска функции, он же - скорость змейки setTimeout(snake, 200); // 200 ms } }
По порядку. При state == true (true/false состояние игры старт/пауза), функция выполняет ряд действий, описание которых приведено в комментариях.
Ниже приведены все остальные функции, которые вызываются в snake().
Коротко по сути: вывод кролика (с генерацией случайных значений его координат и проверкой на существование таких в «теле» змейки);
// вывод кролика на игровое поле function rabbit(){ // задание координат кролику rabbit_pos[0] = math_rand(); rabbit_pos[1] = math_rand(); // проверка на совпадение сгенерированых координат с "телом" змейки for(var i in snake_sectors){ if(rabbit_pos[0] == snake_sectors[i][0]){ rabbit_pos[0] = math_rand(); } if(rabbit_pos[1] == snake_sectors[i][1]){ rabbit_pos[1] = math_rand(); } } // зарисовать сектор, и объявить о наличии живности на поле context.fillRect(rabbit_pos[0], rabbit_pos[1], 9, 9); rabbit_on_field = true; }
функция-генератор случайных значений в заданном диапазоне;
// генерация случайных чисел в заданном диапазоне // для определения координат кролика function math_rand(){ return Math.ceil((Math.random() * 2.9) * 10) * 10; }
проверка на «съедение» кролика (совпадение координат «головы» с координатами кролика);
// проверка на "съедание" кролика // т.е. совпадение координат "головы" змейки с координатами кролика function is_catching(){ if(rabbit_pos[0] == snake_sectors.slice(-1)[0][0] && rabbit_pos[1] == snake_sectors.slice(-1)[0][1]) return true; else return false; }
объявление о конце игры и возврат к настройкам по умолчанию (для нового старта);
// "конец игры", функция вызываемая при наступлении событий - // врезания змейки в себя или края игрового поля function game_over(){ // объявляем конец игры, эта переменная нужна для того, // чтоб заблокировать отклик на нажатие пробела во время заставки "Game Over" g_over = true; // чистим поле от треша context.clearRect(8, 8, 293, 293); // радуем пользователя о конце игры context.font='35px Verdana'; context.strokeStyle="#DB733B"; context.strokeText('Game Over!',47,160); // создаём некую разновидность анимации - задержка надписи "Game Over!" setTimeout(function(){context.clearRect(8, 8, 293, 293); g_over = false}, 1500); // задание дефолтных значений - снова начало игры snake_sectors = [[10, 50], [20, 50], [30, 50], [40, 50]]; state = false; direction = 'right'; start = true; rabbit_on_field = false; }
отлавливание событий нажатия на «игровые» клавиши и соответствующие последствия;
// отлавливание событий нажатия на "игровые" клавиши document.onkeydown = function(event){ var keyCode; if(event == null){ keyCode = window.event.keyCode; } else{ keyCode = event.keyCode; } switch(keyCode){ // space/пробел case 32: // действие при нажатии пробела if(!g_over){ // чередуем паузу со стартом(continue) state = (!state) ? true : false; // вызываем змейку snake(); } break; // left/стрелка влево case 37: // действие при нажатии "влево" if(direction == 'right') return; direction = 'left'; break; // up/стрелка вверх case 38: // действие при нажатии "вверх" if(direction == 'down') return; direction = 'up'; break; // right/стрелка вправо case 39: // действие при нажатии "вправо" if(direction == 'left') return; direction = 'right'; break; // down/стрелка вниз case 40: // действие при нажатии "вниз" if(direction == 'up') return; direction = 'down'; break; default: break; } }
Поиграть можно здесь.
Буду рад услышать здоровую критику в комментах. Благодарю за внимание!
ссылка на оригинал статьи http://habrahabr.ru/post/202014/
Добавить комментарий