Полноценный пример
Дата публикации: 16.03.2011
Уголки — это настольная игра с многовековой историей, у которой существует множество вариаций. В этом примере я реализовал версию для доски 9×9. В начале игры наши фишки находится в нижнем левом углу доски и занимают область 3х3. Цель игры — заполнить в правом верхнем углу область 3х3 всеми своими фишками за минимальное количество ходов.
Передвигать свои фишки можно двумя способами:
- Взять фишку и переместить в соседнюю свободную клетку. Допускаются перемещения на север, юг, восток, запад, северо-запад, северо-восток, юго-запад или юго-восток относительно текущего положения. Доску переворачивать нельзя. Если фишка находится в самой левой колонке, она не может двигаться на запад, северо-запад или юго-запад. Если же находится в самой нижней строке — не может двигаться на юг, юго-восток или юго-запад.
- Взять фишку и перепрыгнуть через соседнюю фишку. Такой прыжок можно повторить, причем, если ты перепрыгнул через соседнюю фишку, а затем сразу есть возможность перепрыгнуть еще через одну — это засчитывается за один ход. Т.е. любое количество прыжков одной фишкой считается за один ход. Т.к. целью игры является минимальное количество ходов, игра сводится к построению цепочек фишек, которые дадут возможность остальным фишкам прыжками преодолеть максимальное расстояние.
Ниже приведен скриншот игры:

А вот тут можно поиграть вживую.
Так как же это работает? Рад, что ты задал этот вопрос. Я не буду приводить весь код целиком тут (его можно увидеть по адресу http://diveintohtml5.org/examples/halma.js). Я пропущу большую часть кода игры, но остановлюсь на некоторых частях, которые отвечают за рисование и за клики мышью на элементе <canvas>.
Во время загрузки страницы мы проводим инициализацию игры, устанавливая размеры <canvas> и получая его контекст.
gCanvasElement.width = kPixelWidth; gCanvasElement.height = kPixelHeight; gDrawingContext = gCanvasElement.getContext("2d");
Сейчас я сделаю кое-что для тебя новое — добавлю событие, которое будет отслеживать клики на холсте:
gCanvasElement.addEventListener("click", halmaOnClick, false);
Функция halmaOnClick() будет вызываться при каждом клике по холсту. Ее аргументом является объект MouseEvent, который содержит информацию, в какой именно точке был совершен клик.
function halmaOnClick(e) { var cell = getCursorPosition(e); // остальное – это посто логика процесса игры for (var i = 0; i
Следующим шагом будем обработка объекта MouseEvent чтобы выяснить по какому квадрату поля был совершен клик. Доска «Уголоков» занимает всю площадь холста, поэтому любой клик приходится на какой-то из квадратов поля. Нужно просто выявить, где он произошел. Это сложно, потому что события мыши реализуются в разных браузерах по-разному.
function getCursorPosition(e) { var x; var y; if (e.pageX || e.pageY) { x = e.pageX; y = e.pageY; } else { x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; }
Сейчас мы получили координаты места клика x и y относительно всей страницы. Но нас интересуют координаты относительно холста. Для этого поступаем следующим образом:
x -= gCanvasElement.offsetLeft; y -= gCanvasElement.offsetTop;
Теперь у нас имеются координаты относительно холста. Например, если x=0 и y=0 мы знаем, что пользователь кликнул по верхнему левому углу холста.
Теперь можно вычислить по какому квадрату доски пользователь кликнул и затем предпринимать соответствующие действия:
var cell = new Cell(Math.floor(y/kPieceWidth), Math.floor(x/kPieceHeight)); return cell; }
Вот так! События мыши — это непросто. Но ты можешь использовать эту же логику во всех своих canvas-приложениях. Запомни: клик мыши → документозависимые координаты → координаты относительно холста → код конкретного приложения.
Хорошо, давай теперь разберем основные типовые задачи рисования. Так как у нас графика простая, я решил при каждом изменении в игре перерисовывать полностью все изображение. Конечно, это не обязательно — контекст будет помнить все что было ранее нарисовано, даже если пользователь прокрутит страницу и холст скроется за пределами экрана или переключит вкладки браузера, а затем вернется назад. Если же ведется разработка сложной графики (например, аркадной игры), то в целях оптимизации производительности можно перерисовывать только измененные участки холста. Но это выходит за рамки данной книги. Следующий код очищает нашу доску:
gDrawingContext.clearRect(0, 0, kPixelWidth, kPixelHeight);
Рисование доски для тебя уже должно быть знакомым: оно сходно с тем как мы рисовали оси координат:
gDrawingContext.beginPath(); /* вертикальные линии */ for (var x = 0; x
Самое интересное начинается во время отрисовки каждой фишки. Фишка представляет собой окружность. С этим мы еще не сталкивались. Кроме того, когда пользователь выбирает фишку, которую будет перемещать, мы для него ее выделим, закрасив круг. В коде аргумент p представляет фишку со свойствами row и column, которые определяют ее положение на доске. Мы используем некоторые игровые константы (столбец, строка) и переводим их в относительные координаты холста (х, у), затем рисуем окружность, а потом (если фишка выбрана), заполняем окружность цветом:
function drawPiece(p, selected) { var column = p.column; var row = p.row; var x = (column * kPieceWidth) + (kPieceWidth/2); var y = (row * kPieceHeight) + (kPieceHeight/2); var radius = (kPieceWidth/2) - (kPieceWidth/10); ...
С логикой игры закончили. Теперь у нас есть координаты (x,y) относительно холста для центра окружности, которую хотим нарисовать. В canvas API нет метода специально для рисования круга, но есть метод arc(). Да и зачем специальный метод, если окружность — это замкнутая дуга. Помнишь основы геометрии? Метод arc() получает в качестве параметров координаты x и y, величину радиуса, начальный и конечный углы (в радианах) и флаг направления (false — по часовой стрелке, true — против часовой). Мы можем использовать модуль Math из Javascript для расчета радиан:
gDrawingContext.beginPath(); gDrawingContext.arc(x, y, radius, 0, Math.PI * 2, false); gDrawingContext.closePath();
Минуточку! Ведь мы так еще ничего и не нарисовали. Методы moveTo(), lineTo(), arc() — все это «карандашные» методы. Чтобы «по-настоящему» нарисовать окружность, нужно установить значение stokeStyle() и вызвать метод stroke() чтобы сделать обводку «чернилами».
gDrawingContext.strokeStyle = "#000"; gDrawingContext.stroke();
А как быть с выбранной фигурой? Можно использовать уже созданный контур для того, чтобы нарисовать границы фигуры и затем залить ее цветом:
чтобы сделать обводку «чернилами».
if (selected) { gDrawingContext.fillStyle = "#000"; gDrawingContext.fill(); }
Все, что касается рисования, мы рассмотрели. Остальная часть программы — это отслеживание верных и не верных ходов, их количества и условия окончания игры. Нам потребовалось всего девять окружностей и событие onClick чтобы создать игру на <canvas>. Ура!
Куда дальше
- следующая — Глава 5. Видео в Веб. Пролог
- предыдущая — А что с IE?
- содержание