Приветствую уважаемое хабра-сообщество. После прочтения поста «Создаём игру, используя canvas и спрайты» в день его выхода, решил углубить свои познания в Canvas. Так, как пока в работе не приходилось сталкиваться с этим элементом, пришлось пробежаться на скорую руку по API.
Конечно, рисование линий, прямоугольников, треугольников и полукругов весьма занимательное занятие. Но для приобретения реального опыта была поставлена задача – создать что-то функциональное и простое.
Вот так родилась идея написать собственную игру, всем знакомую змейку.
К сожалению, работа и отдых занимают столько времени, что игра была закончена лишь сейчас.
Пусть это будет как демонстрация бета-версии игры. Может быть, кому-нибудь пригодится мой труд. Возможно, я получу инвайт, а заодно с интересом послушаю, критические или похвальные мнения коллег.
Поехали!
Код написан с применением библиотеки jQuery, ибо так удобнее. Все переменные и функции объявляются уже после события загрузки странички. Перечисляем переменные и некоторым задаём дефолтные настройки, также задаём размеры холста и определяем «тело» змейки. Которое будет многомерным массивом. Идея состоит в том, что тело змейки – набор секторов с площадью 9px на 9px, и начало координат каждого сектора является число кратное 10. 1px «справа» от сектора не будет зарисовываться, для визуального разделения.
Функция вывода рамок игрового поля, которую вызовем сразу после объявления дефолтных настроек.
Функция вывода змейки, в которой вызываются другие необходимые функции.
По порядку. При state == true (true/false состояние игры старт/пауза), функция выполняет ряд действий, описание которых приведено в комментариях.
Ниже приведены все остальные функции, которые вызываются в snake().
Коротко по сути: вывод кролика (с генерацией случайных значений его координат и проверкой на существование таких в «теле» змейки);
функция-генератор случайных значений в заданном диапазоне;
проверка на «съедение» кролика (совпадение координат «головы» с координатами кролика);
объявление о конце игры и возврат к настройкам по умолчанию (для нового старта);
отлавливание событий нажатия на «игровые» клавиши и соответствующие последствия;
Поиграть можно здесь.
Буду рад услышать здоровую критику в комментах. Благодарю за внимание!
Конечно, рисование линий, прямоугольников, треугольников и полукругов весьма занимательное занятие. Но для приобретения реального опыта была поставлена задача – создать что-то функциональное и простое.
Вот так родилась идея написать собственную игру, всем знакомую змейку.
К сожалению, работа и отдых занимают столько времени, что игра была закончена лишь сейчас.
Пусть это будет как демонстрация бета-версии игры. Может быть, кому-нибудь пригодится мой труд. Возможно, я получу инвайт, а заодно с интересом послушаю, критические или похвальные мнения коллег.
Поехали!
Код написан с применением библиотеки 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 = document.getElementById('mycanvas');
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;
}
}
Поиграть можно здесь.
Буду рад услышать здоровую критику в комментах. Благодарю за внимание!