Выбрать главу

 var Block = function (col, row) {

this.col = col;

this.row = row;

};

// Рисуем квадрат в позиции ячейки

Block.prototype.drawSquare = function (color) {

var x = this.col * blockSize;

var y = this.row * blockSize;

ctx.fillStyle = color;

ctx.fillRect(x, y, blockSize, blockSize);

};

// Рисуем круг в позиции ячейки

Block.prototype.drawCircle = function (color) {

var centerX = this.col * blockSize + blockSize / 2;

17. Пишем игру «Змейка»: часть 2 269

var centerY = this.row * blockSize + blockSize / 2;

ctx.fillStyle = color;

circle(centerX, centerY, blockSize / 2, true);

};

// Проверяем, находится ли эта ячейка в той же позиции, что и ячейка

// otherBlock

Block.prototype.equal = function (otherBlock) {

return this.col === otherBlock.col && this.row === otherBlock.row;

};

// Задаем конструктор Snake (змейка)

 var Snake = function () {

this.segments = [

new Block(7, 5),

new Block(6, 5),

new Block(5, 5)

];

this.direction = "right";

this.nextDirection = "right";

};

// Рисуем квадратик для каждого сегмента тела змейки

Snake.prototype.draw = function () {

for (var i = 0; i < this.segments.length; i++) {

this.segments[i].drawSquare("Blue");

}

};

// Создаем новую голову и добавляем ее к началу змейки,

// чтобы передвинуть змейку в текущем направлении

Snake.prototype.move = function () {

var head = this.segments[0];

var newHead;

this.direction = this.nextDirection;

if (this.direction === "right") {

newHead = new Block(head.col + 1, head.row);

} else if (this.direction === "down") {

newHead = new Block(head.col, head.row + 1);

} else if (this.direction === "left") {

newHead = new Block(head.col - 1, head.row);

} else if (this.direction === "up") {

newHead = new Block(head.col, head.row - 1);

}

if (this.checkCollision(newHead)) {

gameOver();

return;

}

270 Часть III. Графика

this.segments.unshift(newHead);

if (newHead.equal(apple.position)) {

score++;

apple.move();

} else {

this.segments.pop();

}

};

// Проверяем, не столкнулась ли змейка со стеной или собственным

// телом

Snake.prototype.checkCollision = function (head) {

var leftCollision = (head.col === 0);

var topCollision = (head.row === 0);

var rightCollision = (head.col === widthInBlocks - 1);

var bottomCollision = (head.row === heightInBlocks - 1);

var wallCollision = leftCollision || topCollision || 

rightCollision || bottomCollision;

var selfCollision = false;

for (var i = 0; i < this.segments.length; i++) {

if (head.equal(this.segments[i])) {

selfCollision = true;

}

}

return wallCollision || selfCollision;

};

// Задаем следующее направление движения змейки на основе нажатой

// клавиши

Snake.prototype.setDirection = function (newDirection) {

if (this.direction === "up" && newDirection === "down") {

return;

} else if (this.direction === "right" && newDirection === "left") {

return;

} else if (this.direction === "down" && newDirection === "up") {

return;

} else if (this.direction === "left" && newDirection === "right") {

return;

}

this.nextDirection = newDirection;

};

// Задаем конструктор Apple (яблоко)

 var Apple = function () {

this.position = new Block(10, 10);

};

17. Пишем игру «Змейка»: часть 2 271

// Рисуем кружок в позиции яблока

Apple.prototype.draw = function () {

this.position.drawCircle("LimeGreen");

};

// Перемещаем яблоко в случайную позицию

Apple.prototype.move = function () {

var randomCol = Math.floor(Math.random() * (widthInBlocks - 2)) + 1;

var randomRow = Math.floor(Math.random() * (heightInBlocks - 2)) + 1;

this.position = new Block(randomCol, randomRow);

};

// Создаем объект-змейку и объект-яблоко

 var snake = new Snake();

var apple = new Apple();

// Запускаем функцию анимации через setInterval

var intervalId = setInterval(function () {

ctx.clearRect(0, 0, width, height);

drawScore();

snake.move();

snake.draw();

apple.draw();

drawBorder();

}, 100);

// Преобразуем коды клавиш в направления

 var directions = {

37: "left",

38: "up",

39: "right",

40: "down"

};

// Задаем обработчик события keydown (клавиши-стрелки)

$("body").keydown(function (event) {

var newDirection = directions[event.keyCode];

if (newDirection !== undefined) {

snake.setDirection(newDirection);

}

});

Этот код состоит из нескольких частей. Первая часть начинается

со строки , в ней создаются и настраиваются необходимые в коде игры

переменные, включая «холст», контекст рисования, ширину и высоту

«холста» (об этом мы говорили в главе 16). Далее, со строки , идет код

отдельных функций: drawBorder, drawScore, gameOver и circle.

Со строки  начинается код конструктора Block, за которым сле-

дуют его методы drawSquare, drawCircle и equal. Затем, со строки ,

идет код конструктора Snake и все его методы. Далее, со строки , сле-

дует конструктор Apple и его методы draw и move.

272 Часть III. Графика

Наконец, со строки  начинается код, который запускает игру и обес-

печивает ее выполнение. Сначала мы создаем объект-змейку snake

и объект-яблоко apple. Затем с помощью setInterval мы запускаем

анимацию игры. Обратите внимание, что при вызове setInterval мы

сохранили ID интервала в переменной intervalId, чтобы иметь воз-

можность отменить его выполнение в функции gameOver.

Функция, переданная setInterval в качестве аргумента, вызы-

вается на каждом шаге игры. Она отвечает за отображение на «холсте»

всей графики и за обновление состояния игры. Она очищает «холст»

и затем рисует змейку, яблоко и рамку. Также она вызывает метод объек-

та-змейки move, передвигающий змейку на один шаг в текущем направ-

лении. После вызова setInterval со строки  идет код для отслежи-