JavaScript для детей — страница 42 из 48

черед создать объекты для змейки и яблока, а также запрограммировать

обработчик событий клавиатуры, чтобы игрок мог управлять змейкой

с помощью клавиш-стрелок. А в конце главы мы увидим полный код

программы.

При создании кода для змейки и яблока мы воспользуемся объек-

тно-ориентированным подходом, изученным в главе 12, — у каждого

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

и яблока будут использовать функционал более простого объекта, пред-

ставляющего собой ячейку в сетке игрового поля. Начнем с написания

конструктора для простого объекта-ячейки.

Создаем конструктор Block

В этом разделе мы напишем конструктор Block, который будет созда-

вать объекты, представляющие собой отдельные ячейки невидимой

Col —

сетки игрового поля. У каждого объекта-ячейки будут свойства col

column —

и row, определяющие позицию ячейки. На рис. 17.1 показана сетка игро-

столбец

вого поля, а также нумерация строк и столбцов. И хотя сетка не отобра-


Row —

жается на экране, наша игра спроектирована таким образом, чтобы

строка

яблоко и сегменты тела змейки всегда были выровнены по ней.

На рис. 17.1 ячейка с яблоком находится в столбце 10 и строке 10.

А голова змейки (слева от яблока) — в столбце 8 и строке 10.

252

Столбцы

0

10

20

30

39

0

10

20

Cтроки

30

39

Рис. 17.1. Номера столбцов и строк, используемые в конструкторе Block

А вот код конструктора Block:

var Block = function (col, row) {

this.col = col;

this.row = row;

};

Значения строки и столбца передаются в конструктор Block как

аргументы и сохраняются в свойствах col и row нового объекта.

Теперь можно воспользоваться нашим конструктором и создать объ-

ект, соответствующий конкретной ячейке игрового поля. Например,

вот как будет выглядеть создание объекта для ячейки, находящейся

в столбце 5 и строке 5:

var sampleBlock = new Block(5, 5);

Sample block —

шаблон

ячейки

Добавляем метод drawSquare

Наш объект-ячейка позволяет задать позицию в сетке, но, чтобы в дан-

ной позиции что-то появилось, это что-то нужно нарисовать на «холсте».

Следующим этапом мы добавим два метода, drawSquare и drawCircle,

предназначенные для рисования в заданной ячейке квадрата и окружно-

сти. Начнем с drawSquare:

253

Block.prototype.drawSquare = function (color) {

 var x = this.col * blockSize;

 var y = this.row * blockSize;

ctx.fillStyle = color;

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

};

Из главы 12 мы знаем, что, если добавить метод к свойству

конструктора prototype, этот метод будет доступен всем объ-

ектам, созданным при помощи данного конструктора. Поэтому,

добавляя метод drawSquare к Block.prototype, мы делаем

его доступным для всех объектов-ячеек.

Этот метод рисует квадрат в позиции, заданной свойствами

col и row (столбец и строка), и принимает единственный аргумент

color, определяющий цвет. Чтобы нарисовать на «холсте» квадрат,

нужно задать x- и y-координаты его верхнего левого угла, поэтому в стро-

ках  и  мы вычисляем эти значения x и y для нашей ячейки, умножая

ее свойства col и row на blockSize. Затем мы присваиваем свойству кон-

текста рисования fi llStyle значение цвета, переданное в аргументе color.

И наконец, мы вызываем метод ctx.fi llRect, передавая ему вычис-

ленные значения x и y, а также blockSize в качестве ширины и высоты

прямоугольника.

Так можно создать объект-ячейку в столбце 3, строке 4 и нарисовать

там квадрат:

var sampleBlock = new Block(3, 4);

sampleBlock.drawSquare("LightBlue");

На рис. 17.2 показан этот квадрат на «холсте», с пометками, показы-

вающими, как вычисляются его позиция и размер.

Столбцы

0

1

2

3

4

5

6

0

1

this.row × blockSize

2

4 × 10 = 40 пикселей

3

(30, 40)

трокиС 4

10 пикселей (blockSize)

5

10 пикселей

this.col × blockSize

6

3 × 10 = 30 пикселей

Рис. 17.2. Расчет значений для рисования квадрата

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

Добавляем метод drawCircle

Настал черед метода drawCircle. Он очень похож на drawSquare,

однако вместо квадрата рисует заполненную окружность.

Block.prototype.drawCircle = function (color) {

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

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

ctx.fillStyle = color;

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

};

Сначала мы вычисляем x- и y-координаты центра окружности,

создав для этого переменные centerX и centerY. Как и прежде,

мы умножаем свойства col и row на blockSize, но в этот раз еще

и прибавляем blockSize / 2, поскольку нам нужны координаты

центра окружности, который находится в середине ячейки (как

показано на рис. 17.3).

Мы задаем свойству контекста fi llStyle значение аргумента

color, аналогично тому, как делали это для drawSquare, а затем

вызываем уже знакомую нам функцию circle, передавая ей

centerX и centerY в качестве координат, blockSize / 2 в каче-

стве радиуса и true, чтобы окружность была заполнена цветом. Это все та

же функция circle из главы 14, поэтому нужно, как и прежде, добавить

ее определение в код (вы увидите его в полном тексте программы).

Нарисовать окружность в столбце 4, строке 3 можно так:

var sampleCircle = new Block(4, 3);

sampleCircle.drawCircle("LightGreen");

На рис. 17.3 показана наша окружность с вычислениями ее радиуса

и координат центра.

Столбцы

0

1

2

3

4

5

6

0

1

(this.row × blockSize) + (blockSize / 2)

(3 × 10) + (10 / 2)

2

= 35 пикселей

(45, 35)

5 пикселей (blockSize / 2)

3

трокиС 4

(this.col × blockSize) + (blockSize / 2)

(4 × 10) + (10 / 2)

5

= 45 пикселей

6

Рис. 17.3. Расчет значений для рисования окружности

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

Добавляем метод equal

В коде игры нужно будет проверять, не находятся ли два объекта-ячейки

в одной и той же позиции. Например, если позиции яблока и головы

змейки совпадут, это будет означать, что змея съела яблоко. С другой

стороны, если совпадут позиции головы змейки и сегмента ее тела, это

будет означать, что змейка врезалась в собственный хвост.

Чтобы сравнивать позиции ячеек было легче, мы создадим метод

Equal —

equal и добавим его к свойству prototype конструктора Block. Вызов

равняется

equal для некоего объекта-ячейки с другим объектом в качестве аргу-

мента вернет true, если позиции двух объектов-ячеек совпадают

(и false в противном случае). Вот код метода:

Other block —

Block.prototype.equal = function (otherBlock) {

другая ячейка

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

};

Метод equal весьма прост: если свойства col и row двух ячеек (this

и otherBlock) совпадают (то есть this.col равняется otherBlock.

col и this.row равняется otherBlock.row), значит ячейки находятся

в одной позиции и метод вернет true.

Например, создадим два объекта-ячейки под названиями apple

(яблоко) и head (голова) и проверим, совпадают ли их позиции:

var apple = new Block(2, 5);

var head = new Block(3, 5);

head.equal(apple);

false

Свойства row у объектов apple и head совпадают (равны 5), но свой-

ства col различаются. Однако если мы присвоим переменной head

новый объект, расположенный на 1 столбец левее прежнего, метод сооб-

щит нам, что оба объекта находятся в одной позиции:

head = new Block(2, 5);

head.equal(apple);

true

Обратите внимание: не имеет значения, напишем мы head.

equal(apple) или apple.equal(head), в обоих случаях будет выпол-

нено одно и то же сравнение.

Мы воспользуемся методом equal позже, чтобы проверить, не съела

ли змейка яблоко и не столкнулась ли она с собственным хвостом.

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

Создаем змейку

Теперь займемся змейкой. Мы будем хранить ее положение в массиве под

названием segments, состоящем из объектов-ячеек. Чтобы передвинуть

Segment —

змейку, мы будем добавлять новую ячейку в начало массива segments

часть, отрезок

и убирать ячейку с конца. Первый элемент массива будет соответство-

вать змеиной голове.

Пишем конструктор Snake

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

var Snake = function () {

 this.segments = [

new Block(7, 5),

new Block(6, 5),

new Block(5, 5)

];

 this.direction = "right";

 this.nextDirection = "right";

};

Сегменты тела змейки

Свойство segments в строке  — это массив объектов-ячеек, каж-

дый из которых соответствует сегменту змеиного тела. В самом начале

игры этот массив содержит три ячейки в позициях (7, 5), (6, 5) и (5, 5).

На рис. 17.4 изображены эти три начальных сегмента.

Столбцы

0

1

2

3

4

5

6

7

8

0

1

2

3

new Block(6, 5)

троки

new Block(5, 5)

new Block(7, 5)

С

4

5

Хвост

Голова

6

Рис. 17.4. Ячейки, из которых состоит змейка в начале игры

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