В строке мы создаем переменную
leftCollision и присваиваем
ей значение выражения head.col
=== 0. Таким образом, эта перемен-
ная примет значение true, если
змейка столкнется с левой стенкой,
иначе говоря, если голова окажется
в столбце 0. Аналогично перемен-
ная topCollision используется
для проверки строки, в которой находится змеиная голова, чтобы выяс-
нить, не столкнулась ли она с верхней стенкой.
Далее мы проверяем столкновение с правой стенкой, сравнивая стол-
бец, где находится голова со значением widthInBlocks – 1. Поскольку
widthInBlocks равняется 40, по сути, это проверка, не находится
ли голова в столбце 39, который занят правой стенкой, как показано
на рис. 17.1. Затем мы выполняем похожую проверку для нижней стенки
(переменная bottomCollision), проверяя, не равно ли свойство головы
row значению heightInBlocks – 1.
В строке мы определяем, столкнулась ли змейка с какой-
нибудь из стенок: с помощью операции || (ИЛИ) мы проверяем, не соот-
ветствует ли переменная leftCollision, или topCollision, или
rightCollision, или bottomCollision значению true. Полученный
булев результат мы сохраняем в переменной wallCollision.
17. Пишем игру «Змейка»: часть 2 263
Проверка столкновения с собственным телом
Чтобы проверить, не столкнулась ли змейка с собственным телом,
в строке мы создаем переменную selfCollision, сначала присвоив
ей значение false. Затем в строке мы используем цикл for для пере-
бора всех сегментов змеиного тела и проверки, не находится ли какой-
нибудь из сегментов в той же позиции, что и новая голова змейки, — для
этого служит выражение head.equal(this.segments[i]). И голова,
и другие сегменты тела — это объекты-ячейки, поэтому мы можем
использовать для проверки совпадения позиций метод объектов-ячеек
equal. Если обнаружится, что какой-либо из сегментов тела находится
там же, где голова, значит змейка столкнулась сама с собой, и тогда мы
присваиваем selfCollision значение true .
И наконец, в строке мы возвращаем из метода wallCollision ||
selfCollision — это выражение даст true, если змейка столкнулась
либо со стенкой, либо сама с собой.
Управляем змейкой с клавиатуры
Теперь мы напишем код, позволяющий игроку задавать направление
движения змейки с клавиатуры. Мы добавим в программу обработ-
чик событий клавиатуры, который будет определять нажатия кла-
виш-стрелок и менять направление движения в соответствии с нажа-
той клавишей.
Обработчик события keydown
Этот код обрабатывает события клавиатуры:
var directions = {
37: "left",
38: "up",
39: "right",
40: "down"
};
$("body").keydown(function (event) {
var newDirection = directions[event.keyCode];
if (newDirection !== undefined) {
snake.setDirection(newDirection);
}
});
В строке мы создаем объект для преобразования кодов кла-
виш-стрелок в строки, обозначающие направления движения (этот
объект аналогичен объекту keyActions, который мы использовали
264 Часть III. Графика
в разделе «Реакция на нажатия клавиш» на с. 231). В строке мы задаем
обработчик для событий keydown внутри элемента body. Этот обработ-
чик будет вызываться всякий раз, когда пользователь нажмет клавишу
(если сначала он кликнет мышкой по странице браузера).
Первым делом этот обработчик преобразовывает полученный
из объекта-события код клавиши в строку с названием направления
и сохраняет эту строку в переменной newDirection. Если же получен-
ный код клавиши не равен 37, 38, 39 или 40 (это коды клавиш-стрелок),
выражение directions[event.keyCode] вернет undefi ned.
В строке мы сравниваем переменную newDirection с undefi ned
и, если она не равна undefi ned, вызываем метод объекта-змейки
setDirection, передавая ему строку newDirection. (Поскольку в этой
конструкции if нет ветви else, в случае если newDirection равняется
undefi ned, мы просто проигнорируем нажатие клавиши.)
Сейчас этот код не будет работать, поскольку мы еще не определили
метод объекта-змейки setDirection. Давайте это исправим.
Создаем метод setDirection
Метод setDirection принимает от обработчика клавиатуры, код кото-
рого мы только что разбирали, строку с направлением и использует ее,
чтобы установить новое направление движения змейки. Кроме того,
этот метод не дает игроку менять направление такими способами, после
которых змейка сразу же врезается сама в себя. Например, если змейка,
двинувшись вправо, тут же начнет двигаться влево, не повернув перед
этим вверх или вниз, чтобы освободить себе путь, она мгновенно вре-
жется сама в себя. Мы будем называть такие смены направления недопу-
стимыми, поскольку мы не хотим давать игроку возможность их совер-
шать. Например, на рис. 17.6 для движения змейки вправо показано два
допустимых направления и одно недопустимое.
Текущее
Допустимые
направление
новые
направления
Текущее
Недопустимое
направление
новое
направление
Рис. 17.6. Допустимые направления относительно текущего
17. Пишем игру «Змейка»: часть 2 265
Метод setDirection проверяет, не пытается ли игрок выбрать недо-
пустимое направление. Когда это так, метод использует команду return
для досрочного выхода. В противном случае он обновляет свойство объ-
екта-змейки nextDirection.
Вот код метода setDirection:
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;
};
Конструкция if... else в строке состоит из четырех частей — для
обработки четырех недопустимых смен направления, которые мы хотим
предотвратить. В первой части говорится, что, если змейка движется
вверх (this.direction равняется "up"), а игрок нажал стрелку вниз
(newDirection равняется "down"), мы должны совершить досрочный
выход из метода с помощью return. Остальные части конструкции обра-
батывают другие недопустимые смены направления тем же образом.
Последняя строка метода setDirection будет выполнена, только
если в newDirection находится допустимое новое направление —
иначе до нее дело не дойдет, потому что одна из команд return в преды-
дущих строках завершит выполнение метода.
Если направление newDirection является допустимым, мы присва-
иваем его свойству nextDirection в строке .
Создаем яблоко
В этой игре яблоко представляет собой объект из трех компонентов: это
свойство position, которое хранит позицию яблока в виде объекта-
ячейки, метод draw, с помощью которого мы будем рисовать яблоко
на экране, и метод move, который нужен, чтобы задать яблоку новую
позицию после того, как змейка его съела.
Пишем конструктор Apple
Все, что делает конструктор, — это задает свойство position, присваи-
вая ему новый объект-ячейку.
266 Часть III. Графика
var Apple = function () {
this.position = new Block(10, 10);
};
Этот код создает новый объект-ячейку в столбце 10, строке 10 и при-
сваивает его свойству объекта-яблока position. Мы будем использовать
этот конструктор для создания объекта-яблока в самом начале игры.
Рисуем яблоко
Чтобы нарисовать яблоко, создадим метод draw:
Apple.prototype.draw = function () {
this.position.drawCircle("LimeGreen");
};
Этот метод очень прост, всю работу за него выполняет метод drawCircle
(созданный в разделе «Добавляем метод drawCircle» на с. 255). Чтобы
нарисовать яблоко, мы просто вызываем для хранящего объект-ячейку
свойства position метод drawCircle, передавая ему название цвета
"LimeGreen" (темно-зеленый) для отображения в заданной ячейке
зеленого кружка.
Чтобы проверить работу этого метода, выполните такой код:
var apple = new Apple();
apple.draw();
Перемещаем яблоко
Метод move перемещает яблоко в случайную новую позицию на игро-
вом поле (то есть в любую ячейку на «холсте», кроме области рамки).
Мы будем вызывать этот метод всякий раз, когда змейка съест яблоко,
чтобы оно появилось снова в другой позиции.
Apple.prototype.move = function () {
var randomCol = Math.floor(Math.random() * (widthInBlocks - 2)) + 1;
Random col —
var randomRow = Math.floor(Math.random() * (heightInBlocks - 2)) + 1;
случайный
this.position = new Block(randomCol, randomRow);
столбец
};
Random row —
случайная
В строке и следующей строке мы создаем переменные randomCol
строка
и randomRow, которые примут значения, соответствующие случайному
столбцу и случайной строке на игровом поле. Как показано на рис. 17.1
на с. 253, столбцы и строки игрового поля имеют номера от 1 до 38, сле-
довательно, нам нужно выбрать случайные числа из этого диапазона.
17. Пишем игру «Змейка»: часть 2 267
Для получения этих чисел мы воспользуемся выражением Math.
fl oor(Math.random() * 38), которое возвращает случайное число
от 0 до 37, и затем прибавим к нему 1, чтобы получить число от 1 до 38
(как работают методы Math.fl oor и Math.random, см. в разделе