Змейка на Canvas

от автора

Приветствую уважаемое хабра-сообщество. После прочтения поста «Создаём игру, используя canvas и спрайты» в день его выхода, решил углубить свои познания в Canvas. Так, как пока в работе не приходилось сталкиваться с этим элементом, пришлось пробежаться на скорую руку по API.
Конечно, рисование линий, прямоугольников, треугольников и полукругов весьма занимательное занятие. Но для приобретения реального опыта была поставлена задача – создать что-то функциональное и простое.

Вот так родилась идея написать собственную игру, всем знакомую змейку.

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

Поехали!
Код написан с применением библиотеки 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *