В этом руководстве (первом из серии) вы узнаете основы разработки игр на HTML5 с использованием JavaScript и элемента canvas
. Вам не нужно иметь опыт программирования или даже опыт HTML (кроме этой статьи ). Давайте начнем!
Вступление
Было бы трудно пропустить статьи «HTML5 против Flash», которые появились по всему Интернету, особенно после того, как в прошлом году Стив Джобс подумал о Flash , а на этой неделе объявил Microsoft о том, что веб-браузер Windows 8 не будет поддерживать Flash на планшетах. по умолчанию. Я не собираюсь вступать в эту дискуссию здесь; Как бы то ни было, изучение HTML5 не повредит. Даже если вы знаете Flash, вам не помешает иметь в своем комплекте другой инструмент.
Это руководство не требует, чтобы вы знали Flash или имели опыт программирования. На самом деле, все, что вам нужно знать перед началом работы, объясняется в этой единственной статье: ускорение работы с HTML . Это оно! Если вы можете следовать этому, вы можете следовать этому.
Я основываю эту серию на моей серии учебных пособий по Flash , которая, в свою очередь, основана на еще более старой учебной программе по Flash от парня по имени FrozenHaddock (которому я очень благодарен за то, что позволил мне использовать его идеи). Тем не менее, это не прямой порт для любого учебника; Я буду полностью переписывать код и объяснения в соответствии с HTML5.
Пара заметок:
- Кросс-браузерная совместимость — это реальная и важная проблема в веб-разработке, но мы будем немного эгоистичны и сосредоточимся на том, чтобы наша игра работала только в одном браузере: Chrome . Будьте уверены, мы будем иметь дело с другими браузерами (в том числе мобильными) в других учебниках по Activetuts +, но пока мы будем придерживаться Chrome, чтобы нам не приходилось разделять наше внимание.
- Чистый код важнее в HTML5, чем во многих других платформах, потому что базовый язык программирования (JavaScript) позволит вам многое сойти с рук. Итак, мы позаботимся о том, чтобы вы привыкли писать приличный код … в конце концов. Вначале мы немного запутаемся, просто для того, чтобы все пошло как по маслу и не заставляли вас прокручивать тысячи слов о «лучших практиках», прежде чем что- то делать .
В этой первой части урока мы просто настроим все и добавим некоторые базовые игровые механики. В будущих частях будет добавлено несколько появившихся врагов, рекорды, экраны меню, несколько уровней и все такое.
Хватит разговоров — начнем!
Настройка
Первое, что нужно сделать, это создать файл .html. Вы можете использовать для этого простой текстовый редактор или потратить несколько сотен долларов на программное обеспечение, специально разработанное для разработки HTML; лично я бы сейчас придерживался свободного программного обеспечения. Вот три рекомендации: TextEdit (для Mac), Notepad ++ (для Windows) и Sublime Text 2 (для Windows, OS X и Linux). Выбирайте.
Создайте новый файл и введите следующее:
1
2
3
4
5
6
7
8
9
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
</head>
<body>
</body>
</html>
|
Если вы не понимаете, что из этого получается, прочитайте мое основное руководство по HTML .
Создайте на жестком диске новую папку с именем AvoiderGame и сохраните в ней этот HTML-файл как game.html . Если вы загрузите его прямо сейчас, он просто покажет пустую белую страницу (как вы знаете), поэтому поместите туда абзац текста, чтобы убедиться, что все в порядке. Я добавлю ссылку на этот урок, но вы можете ввести что угодно — ваше имя и веб-сайт, возможно?
1
2
3
4
5
6
7
8
9
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
</head>
<body>
<p>From <a href=»http://active.tutsplus.com/tutorials/html5/learn-html5-with-this-simple-avoider-game-tutorial» rel=»external»>Learn HTML5 With This Simple Avoider Game Tutorial</a>.</p>
</body>
</html>
|
JavaScript
Хорошо, теперь вы не будете удивлены, узнав, что мы скоро напишем немного JavaScript — помните, JavaScript позволяет веб-страницам что- то делать , и это именно то, что нам нужно для создания игр. Мы поместим весь наш JavaScript во внешний файл, чтобы все было аккуратно, и поместим этот файл в отдельную папку, чтобы все было еще аккуратнее.
Итак, создайте новую папку с именем js внутри вашей папки AvoiderGame . Затем создайте новый пустой текстовый файл и сохраните его как main.js внутри этой новой папки AvoiderGame / js / .
Измените ваш HTML, чтобы ссылаться на этот файл JS:
01
02
03
04
05
06
07
08
09
10
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
<script src=»js/main.js»></script>
</head>
<body>
<p>From <a href=»http://active.tutsplus.com/tutorials/html5/learn-html5-with-this-simple-avoider-game-tutorial» rel=»external»>Learn HTML5 With This Simple Avoider Game Tutorial</a>.</p>
</body>
</html>
|
Обратите внимание, что я не написал src="http://active.tutsplus.com/...whatever.../js/main.js"
или src="C:\AvoiderGame\js\main.js"
; таким образом, мы говорим странице HTML: «найдите папку js в том же каталоге, что и вы, а затем используйте файл main.js, который находится внутри нее». Это называется относительный путь .
Если вы хотите проверить, что это работает, поставьте alert("Working!");
в вашем файле JS, затем загрузите страницу HTML. Если вы получаете диалоговое окно, все в порядке.
01
02
03
04
05
06
07
08
09
10
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
<script src=»js/main.js»></script>
</head>
<body>
<p>From <a href=»http://active.tutsplus.com/tutorials/html5/learn-html5-with-this-simple-avoider-game-tutorial» rel=»external»>Learn HTML5 With This Simple Avoider Game Tutorial</a>.</p>
</body>
</html>
|
CSS
Пока мы это делаем, давайте скомпонуем внешний CSS-файл; мы можем использовать его, чтобы текст выглядел лучше, и нам может понадобиться использовать CSS в игре позже.
Создайте новую папку внутри AvoiderGame с именем css , а затем создайте в ней новый пустой текстовый файл с именем style.css . Измените ваш HTML следующим образом:
01
02
03
04
05
06
07
08
09
10
11
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
<script src=»js/main.js»></script>
<link rel=»stylesheet» href=»css/style.css» />
</head>
<body>
<p>From <a href=»http://active.tutsplus.com/tutorials/html5/learn-html5-with-this-simple-avoider-game-tutorial» rel=»external»>Learn HTML5 With This Simple Avoider Game Tutorial</a>.</p>
</body>
</html>
|
Я собираюсь изменить свой CSS-файл, чтобы он соответствовал некоторым стилям, которые мы часто используем на демонстрационных страницах здесь, в Activetuts +; не стесняйтесь копировать его, придумать свой или оставить пустым:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
body {
background: #ffffff;
text-align: center;
padding: 20px;
color: #575757;
font: 14px/21px Arial,Helvetica,sans-serif;
}
a {
color: #B93F14;
}
a:hover {
text-decoration: none;
}
ol {
width: 600px;
text-align: left;
margin: 15px auto
}
|
Это руководство не о CSS, поэтому, если вы этого не понимаете, не беспокойтесь об этом. (Если вам интересно, вы можете посмотреть значение этих атрибутов CSS на W3Schools.com .)
Хорошо, это скучная установка с дороги. Вы можете увидеть, как выглядит страница, нажав здесь , и вы можете скачать весь исходный код в ZIP-файле здесь . Давайте создадим наш аватар!
Получи свою голову в игре
Нам нужно изображение, которое будет представлять персонажа нашего игрока в этой игре. Используйте все, что вам нравится — фотографию вашего лица, ваш аватар в Твиттере, рисунок, который вы нарисовали, — но убедитесь, что у него прозрачный фон, он приблизительно круглый и имеет размер 30×30 пикселей.
Оригинальный учебник, на котором основан этот, использовал череп. Я не уверен, почему, но я подозреваю, что это была попытка подорвать типичную позицию игры против скелета; в конце концов, под нашей кожей, не у каждого из нас есть череп?
Я не из тех, кто нарушает традиции, поэтому я тоже буду использовать череп. Вы можете скачать мой, щелкнув по нему правой кнопкой мыши, если вы не хотите делать свой собственный:
И прежде чем спросить: да, я доступен для комиссии.
Что бы вы ни выбрали, сохраните его как avatar.png в новой папке с именем img в AvoiderGame . Структура вашей папки теперь выглядит так:
1
2
3
4
5
6
7
8
|
/AvoiderGame/
game.html
/css/
style.css
/js/
main.js
/img/
avatar.png
|
Итак, как мы можем включить это в нашу игру? Если вы обратили внимание, вы, вероятно, предложите это:
1
|
<img src=»img/avatar.png» alt=»Avatar» />
|
И это правда, что бы поставить аватар на нашей странице! Но это не то, что мы собираемся использовать.
холст
Элемент img
показывает одно изображение, загруженное из файла PNG или JPG (или любого другого). Тег canvas
, новый для HTML5, может динамически генерировать изображение, состоящее из других изображений меньшего размера, текста, примитивных фигур и многого другого, если хотите. Его содержимое может быть изменено в любой момент, так что вы можете создать иллюзию движения или взаимодействия, если вы изменяете содержимое в соответствии с действиями пользователя.
Мы создаем холст так же, как и любой другой элемент HTML:
01
02
03
04
05
06
07
08
09
10
11
12
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
<script src=»js/main.js»></script>
<link rel=»stylesheet» href=»css/style.css» />
</head>
<body>
<canvas></canvas>
<p>From <a href=»http://active.tutsplus.com/tutorials/html5/learn-html5-with-this-simple-avoider-game-tutorial» rel=»external»>Learn HTML5 With This Simple Avoider Game Tutorial</a>.</p>
</body>
</html>
|
… хотя, если вы посмотрите на это , вы ничего не увидите там. Он невидим, поэтому единственный эффект, который он имеет, — это немного сдвинуть текст.
С помощью CSS мы можем дать ему схему, чтобы мы могли отличить ее от фона. Добавьте это к вашему CSS:
1
2
3
|
canvas {
border: 1px solid black;
}
|
Проверьте это . Хотя это немного мало; давайте сделаем это 400 на 300 пикселей (телевизионные размеры старой школы):
01
02
03
04
05
06
07
08
09
10
11
12
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
<script src=»js/main.js»></script>
<link rel=»stylesheet» href=»css/style.css» />
</head>
<body>
<canvas width=»400″ height=»300″></canvas>
<p>From <a href=»http://active.tutsplus.com/tutorials/html5/learn-html5-with-this-simple-avoider-game-tutorial» rel=»external»>Learn HTML5 With This Simple Avoider Game Tutorial</a>.</p>
</body>
</html>
|
Так лучше Теперь я сказал, что мы можем динамически добавлять изображения на холст, так что давайте сделаем это дальше.
функции
Помните, в руководстве по HTML я показал вам, как добиться успеха, когда вы нажимаете HTML-элементы? Вот краткий обзор:
01
02
03
04
05
06
07
08
09
10
11
12
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
<script src=»js/main.js»></script>
<link rel=»stylesheet» href=»css/style.css» />
</head>
<body>
<canvas onclick=»alert(‘Clicked the canvas’);»
<p>From <a href=»http://active.tutsplus.com/tutorials/html5/learn-html5-with-this-simple-avoider-game-tutorial» rel=»external»>Learn HTML5 With This Simple Avoider Game Tutorial</a>.</p>
</body>
</html>
|
Если вы нажмете на холст, вы получите сообщение в диалоговом окне. Это потому, что alert()
— это функция JavaScript : это ярлык для нескольких строк кода. Мы можем написать наши собственные функции в нашем файле JS. Откройте main.js и введите следующее:
1
2
3
4
5
|
function alertSeveralTimes() {
alert(«Hello!»);
alert(«Look, we can run several messages in a row.»);
alert(«Annoyed yet?»);
}
|
(Удалить исходное alert("Working!");
Если вы еще этого не сделали.)
Вы видите, как это работает? Мы создали новую function
alertSeveralTimes()
, содержимое которой находится внутри фигурных скобок ( {
и }
). Когда мы сообщаем браузеру запустить эту alertSeveralTimes()
, он будет запускать каждое из предупреждений по очереди, одно за другим.
Давайте попробуем это:
01
02
03
04
05
06
07
08
09
10
11
12
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
<script src=»js/main.js»></script>
<link rel=»stylesheet» href=»css/style.css» />
</head>
<body>
<canvas onclick=»alertSeveralTimes();»
<p>From <a href=»http://active.tutsplus.com/tutorials/html5/learn-html5-with-this-simple-avoider-game-tutorial» rel=»external»>Learn HTML5 With This Simple Avoider Game Tutorial</a>.</p>
</body>
</html>
|
Попробуй это ! Мы эффективно сгруппировали несколько функций alert()
в одну большую функцию, называемую alertSeveralTimes()
, и сказали, что она запускается при каждом нажатии на холст.
Возможно, вы удивляетесь, почему alert("Working!")
alertSeveralTimes()
как только мы открыли страницу, а вот alertSeveralTimes()
этого не сделало, хотя они оба находились в одном месте (вверху main.js ). Это из-за этой волшебной ключевой function
; когда браузер видит это, он не думает: «ага, это какой-то код, который я должен запустить немедленно!», он думает: «ага, это какой-то код, который я должен связать в новую функцию, которую я могу запустить позже !»
Тем не мение. Теперь давайте заставим нашу функцию что-то сделать с канвой. Заставить его загрузить изображение немного сложно, поэтому мы начнем с чего-то более простого: изменение его размера.
Модификация холста
Одна из самых удивительных особенностей JavaScript — это возможность изменять HTML-код страницы. Проверь это; измените ваш файл JS так, чтобы он содержал это:
1
2
3
4
|
function changeCanvasSize() {
gameCanvas.width = 600;
gameCanvas.height = 800;
}
|
Вероятно, вы можете догадаться, что это делает: он берет элемент canvas и изменяет его атрибуты width
и height
(нам не нужно использовать кавычки вокруг чисел в JavaScript, в отличие от атрибутов HTML).
Кроме … как он узнает, что gameCanvas
ссылается на холст, который есть на нашей странице?
Ну, это не … пока. Мы должны заставить это понять это.
Во-первых, мы должны дать элементу холста атрибут id
(сокращение от «идентификации»); это просто имя, которое мы используем, чтобы позже к нему можно было обратиться в JavaScript:
01
02
03
04
05
06
07
08
09
10
11
12
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
<script src=»js/main.js»></script>
<link rel=»stylesheet» href=»css/style.css» />
</head>
<body>
<canvas id=»gameCanvas» onclick=»alertSeveralTimes();»
<p>From <a href=»http://active.tutsplus.com/tutorials/html5/learn-html5-with-this-simple-avoider-game-tutorial» rel=»external»>Learn HTML5 With This Simple Avoider Game Tutorial</a>.</p>
</body>
</html>
|
Пока мы на этом, давайте сделаем так, чтобы атрибут onclick
указывал на нашу новую changeCanvasSize()
:
01
02
03
04
05
06
07
08
09
10
11
12
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
<script src=»js/main.js»></script>
<link rel=»stylesheet» href=»css/style.css» />
</head>
<body>
<canvas id=»gameCanvas» onclick=»changeCanvasSize();»
<p>From <a href=»http://active.tutsplus.com/tutorials/html5/learn-html5-with-this-simple-avoider-game-tutorial» rel=»external»>Learn HTML5 With This Simple Avoider Game Tutorial</a>.</p>
</body>
</html>
|
Этого все еще не достаточно. Мы должны сообщить JavaScript, что он имеет дело с элементом со страницы HTML (или «документ HTML», как это более правильно известно):
1
2
3
4
|
function changeCanvasSize() {
document.getElementById(«gameCanvas»).width = 600;
document.getElementById(«gameCanvas»).height = 800;
}
|
Теперь, я знаю, это не совсем логично. Почему gameCanvas
неожиданно gameCanvas
в кавычках? Почему мы используем document.getElementById("gameCanvas")
вместо просто, скажем, getDocumentElement("gameCanvas")
или даже document.gameCanvas
? Обещаю, все это станет ясно во время серии уроков, но сейчас, пожалуйста, продолжайте.
Проверьте свой новый код . Холст изменяет свой размер, как только вы нажимаете на него. Потрясающие!
Теперь я должен предупредить вас: программисты ленивы. Мы ненавидим писать один и тот же код снова и снова, и если есть какой-то способ уменьшить требуемую типизацию, мы его возьмем. Итак, позвольте мне представить вам краткий способ ссылки на холст:
1
2
3
4
5
|
function changeCanvasSize() {
var gameCanvas = document.getElementById(«gameCanvas»);
gameCanvas.width = 600;
gameCanvas.height = 800;
}
|
Видишь, как это работает? Как и в ключевом слове function
: «эй, заверните весь этот код под именем changeCanvasSize()
, пожалуйста», ключевое слово var
говорит: «эй, используйте слово gameCanvas
для ссылки на элемент HTML с идентификатором« gameCanvas » , пожалуйста». Затем (в строках 3 и 4 выше) мы можем использовать эту новую сокращенную gameCanvas
для более длинного document.getElementById("gameCanvas")
— потому что они ссылаются на одно и то же .
Это важно: мы не создали новый холст; мы только что заставили gameCanvas
ссылаться на существующий элемент canvas.
Тем не менее, можно использовать var
чтобы создать что-то новое …
Нажмите, чтобы Череп
Как я уже сказал, мы движемся к добавлению изображения на (в настоящее время пустой) холст. Но прежде чем мы сможем это сделать, мы должны загрузить изображение. И прежде чем мы сможем это сделать, нам нужно что-то загрузить в изображение.
Модифицируйте свой JS так:
1
2
3
4
5
6
7
|
function changeCanvasSize() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
gameCanvas.width = 600;
gameCanvas.height = 800;
}
|
(Я добавил пустую строку, чтобы четко отделить операторы var
от остальных.)
Здесь мы снова используем ключевое слово var
но на этот раз оно говорит о чем-то немного ином: «эй, создайте новый объект Image
и используйте слово avatarImage
чтобы ссылаться на него отныне, пожалуйста». Объект Image
в основном похож на элемент img
; принципиальное отличие здесь в том, что это не в HTML . Мы создали этот новый элемент, но его нет нигде в HTML; он просто плавает в эфире JavaScript. Я нахожу это немного странным.
Как и элемент img
на странице, это Image
в значительной степени бесполезно без установки его src
, так что сделайте следующее:
1
2
3
4
5
6
7
8
9
|
function changeCanvasSize() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
gameCanvas.width = 600;
gameCanvas.height = 800;
avatarImage.src = «img/avatar.png»;
}
|
(Еще раз я использую пустую строку, чтобы отделить куски кода, которые делают разные вещи друг от друга (как абзацы в тексте), и еще раз я использую относительный путь для ссылки на местоположение файла.)
Итак, сейчас идет загрузка изображения, но вам придется поверить моему слову на данный момент, так как оно все еще там, в эфире, где мы его не видим. Тем не менее, мы можем проверить его другие атрибуты:
01
02
03
04
05
06
07
08
09
10
|
function changeCanvasSize() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
gameCanvas.width = 600;
gameCanvas.height = 800;
avatarImage.src = «img/avatar.png»;
alert(avatarImage.width);
}
|
Мы говорим, чтобы показать нам диалоговое окно, содержащее значение атрибута width
нашего изображения. Проверьте это в своем коде и посмотрите, что вы получите; Я получаю 29 , что совершенно верно.
С помощью еще одной строки кода мы можем нарисовать аватар на холсте:
01
02
03
04
05
06
07
08
09
10
|
function changeCanvasSize() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
gameCanvas.width = 600;
gameCanvas.height = 800;
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, 0, 0);
}
|
Давайте разберем это:
-
gameCanvas.getContext("2d")
: мы не рисуем непосредственно на холсте, мы рисуем то, что называется «контекстом рисования»; это позволяет нам определить, хотим ли мы рисовать в 2D или 3D. Хорошо, в данный момент нет 3D-контекста, но это позволяет нам планировать будущее. -
drawImage()
: довольно просто. Это функция, которая позволяет нам рисовать изображение в контексте холста. -
avatarImage
: Это объект изображения, который мы плавали в эфире, помните? -
0, 0
: это координаты, по которым мы хотим нарисовать изображение. В школе вас учат, что (0, 0) — это левый нижний угол страницы; на компьютере это верхний левый угол (ось x направлена вправо, а ось y направлена вниз).
Посмотри . Оно работает! (Если это не так, помните, что вы должны просматривать это в Chrome ; я не гарантирую, что это будет работать в любом другом браузере.)
Несколько черепов
Функция drawImage()
работает как штамп картошки:
Он просто берет содержимое объекта изображения и клонирует его на холст; Конечно, мы имеем дело с пикселями, а не с краской, но вы поняли.
Это означает, что мы можем добавить несколько черепов на холст, например так:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
function changeCanvasSize() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
gameCanvas.width = 600;
gameCanvas.height = 800;
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, 0, 0);
gameCanvas.getContext(«2d»).drawImage(avatarImage, 100, 50);
gameCanvas.getContext(«2d»).drawImage(avatarImage, 200, 130);
gameCanvas.getContext(«2d»).drawImage(avatarImage, 300, 270);
}
|
Проверьте это, партия черепа . Мы также можем сделать так, чтобы череп появлялся в произвольном месте каждый раз:
01
02
03
04
05
06
07
08
09
10
|
function changeCanvasSize() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
gameCanvas.width = 600;
gameCanvas.height = 800;
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
}
|
Math.random()
дает вам случайное число от 0 до 1, поэтому Math.random() * 100
дает вам случайное число от 0 до 100; это означает, что координаты нового черепа находятся где-то между (0, 0) и (100, 100). Посмотри !
Но подождите — почему сейчас только один череп? Это как-то связано с тем, что это новая функция? Холст очищается каждый раз, когда вы запускаете функцию?
Нет. Холст очищается каждый раз, когда вы изменяете его высоту или ширину, даже если вы не меняете их. Итак, если мы изменим наш JS так:
01
02
03
04
05
06
07
08
09
10
|
function changeCanvasSize() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
}
|
… тогда мы можем продолжать добавлять новые черепа .
Фактически, давайте изменим имя функции на drawAvatar()
, и немного drawAvatar()
порядок:
1
2
3
4
5
6
7
|
function drawAvatar() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
}
|
Не забудьте изменить атрибут onclick
для canvas
в вашем HTML:
01
02
03
04
05
06
07
08
09
10
11
12
|
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Avoider Game</title>
<script src=»js/main.js»></script>
<link rel=»stylesheet» href=»css/style.css» />
</head>
<body>
<canvas id=»gameCanvas» onclick=»drawAvatar();»
<p>From <a href=»http://active.tutsplus.com/tutorials/html5/learn-html5-with-this-simple-avoider-game-tutorial» rel=»external»>Learn HTML5 With This Simple Avoider Game Tutorial</a>.</p>
</body>
</html>
|
Хорошо, теперь давайте передвинем этот аватар.
Добавление интерактивности
Я хочу, чтобы аватар следовал за мышью. Мы можем использовать тот же принцип, что и аниматоры: если мы продолжим стирать содержимое холста, а затем заново рисовать аватар в другой позиции, аватар будет двигаться. Поэтому все, что нам нужно сделать, это продолжать перерисовывать аватар по координатам мыши, и мы готовы!
Как мы это делаем, хотя?
Грандиозное событие
Судя по тому, что мы сделали до сих пор, вы можете догадаться, что мы добавили бы к onmousemove
атрибут события onmousemove
(который будет срабатывать каждый раз, когда пользователь перемещал указатель мыши), а затем заставили его запускать drawAvatar()
, но конкретно в текущие координаты мыши. Это вдохновляет, но, к сожалению, на самом деле не работает, просто потому что не предлагает простой способ получить координаты мыши.
Тем не менее, это очень близко к тому, что мы хотим сделать! Взгляните на это:
1
2
3
4
5
6
7
8
9
|
function drawAvatar() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
gameCanvas.addEventListener(«mousemove», redrawAvatar);
}
|
Это примерно соответствует предложению выше; redrawAvatar()
(которую мы еще не написали) будет вызываться всякий раз, когда мышь перемещается по холсту. Но есть одна большая разница.
Обратите внимание, как мы пишем redrawAvatar
, а не redrawAvatar()
в приведенном выше коде, тогда как на нашей HTML-странице мы помещаем drawAvatar()
— с «круглыми скобками» — в атрибут события onclick
нашего canvas. Причина этого немного сложна (хотя вы поймете к концу серии), но у нее есть один очень важный результат: он позволяет нам получить координаты мыши.
Когда мышь перемещается, браузер создает новый объект — так же, как когда мы ранее создали новое изображение в нашем JavaScript. Этот объект имеет определенные атрибуты, которые имеют отношение к тому, что вызвало его создание; в этом случае, поскольку мышь перемещена, она содержит координаты мыши. Brilliant!
Итак, как нам получить к нему доступ? Что ж, этот новый объект (который называется MouseEvent
по причинам, которые вы можете догадаться) передается в redrawAvatar()
. Э-э, но мы этого еще не написали, поэтому давайте сделаем это сейчас. Добавьте этот код в ваш файл JS:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
function drawAvatar() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
gameCanvas.addEventListener(«mousemove», redrawAvatar);
}
function redrawAvatar(mouseEvent) {
}
|
Ага — на этот раз способ определения функции немного отличается: мы добавили слово mouseEvent
между этими скобками. Это происходит потому, что мы ожидаем, что браузер передаст объект MouseEvent
нашей новой функции, так же, как когда мы передали координаты функции drawImage()
.
Поскольку мы дали ему имя, мы можем получить доступ к атрибутам этого нового объекта:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
function drawAvatar() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
gameCanvas.addEventListener(«mousemove», redrawAvatar);
}
function redrawAvatar(mouseEvent) {
alert(mouseEvent.x);
}
|
Проверьте это ; вам нужно будет щелкнуть холст, прежде чем что-то произойдет, потому что именно внутри этой функции мы заставляем браузер обращать внимание на движения мыши.
Вы заметите, что диалоговое окно появляется только тогда, когда мышь перемещается над элементом canvas. Вы также можете заметить что-то странное в этом числе: оно слишком большое! Я получаю числа более 900 , хотя ширина холста всего 400.
Это связано с тем, что mouseEvent.x
задает положение мыши по x от края страницы , а не от края холста. Мы можем использовать mouseEvent.offsetX
чтобы получить x-позицию мыши от края холста:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
function drawAvatar() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
gameCanvas.addEventListener(«mousemove», redrawAvatar);
}
function redrawAvatar(mouseEvent) {
alert(mouseEvent.offsetX);
}
|
Итак, recap: addEventListener()
заставляет браузер прослушивать определенные события — например, движение мыши — и затем запускать функцию, когда это событие «слышно». Браузер создает новый объект (например, MouseEvent
) и передает его этой функции.
Немного трудно обернуть голову, но не волнуйся; мы будем использовать его очень часто, так что вы освоите его!
Двигай головой
У нас почти есть движение. На самом деле, я рекомендую вам сделать так, чтобы аватар следовал за мышью самостоятельно, прежде чем читать дальше. Вы, вероятно, очень близко подойдете!
Однако есть одна большая вещь, которая вас озадачит: слово var
которое, как вы помните, вы можете использовать, чтобы установить сокращение, только «длится» внутри функции, в которой оно было определено.
Это означает, что если вы попытаетесь это сделать, скажите:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
function drawAvatar() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
gameCanvas.addEventListener(«mousemove», redrawAvatar);
}
function redrawAvatar(mouseEvent) {
gameCanvas.width = 400;
alert(mouseEvent.offsetX);
}
|
… это не сработает, потому что gameCanvas
ничего не значит вне drawAvatar()
!
Так что, если вы не получили его в первый раз, попробуйте еще раз.
Мой код здесь, если вы хотите проверить свой:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
function drawAvatar() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
gameCanvas.addEventListener(«mousemove», redrawAvatar);
}
function redrawAvatar(mouseEvent) {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
}
|
Тада ! Ой, подожди, черт, я забыл стереть холст, изменив его ширину или высоту. Классный эффект, но давайте попробуем это снова:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
function drawAvatar() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
gameCanvas.addEventListener(«mousemove», redrawAvatar);
}
function redrawAvatar(mouseEvent) {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.width = 400;
gameCanvas.getContext(«2d»).drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
}
|
Попробуйте сейчас . Успех!
Подожди, для чего это было?
Когда вы несколько раз посмотрите на свой код через несколько дней, вы, вероятно, забудете, для чего он нужен. В частности, я подозреваю, что вы забудете, почему вам нужно изменить размер холста.
К счастью, есть способ напомнить себе: комментарии.
Посмотри на это:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
function drawAvatar() {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.getContext(«2d»).drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
gameCanvas.addEventListener(«mousemove», redrawAvatar);
}
function redrawAvatar(mouseEvent) {
var gameCanvas = document.getElementById(«gameCanvas»);
var avatarImage = new Image();
avatarImage.src = «img/avatar.png»;
gameCanvas.width = 400;
gameCanvas.getContext(«2d»).drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
}
|
Браузер игнорирует все в строке после //
. Это означает, что вы можете писать там, что вам нравится, и он не будет выполняться как код. Это называется встроенным комментарием и очень важно. Чтобы помочь себе привыкнуть комментировать свой код, просмотрите его сейчас и пишите комментарии после каждой строки, которые, по вашему мнению, могут быть затруднены для понимания после нескольких дней перерыва.
Комментирование может показаться пустой тратой времени. Я думаю, что многие новые программисты предполагают, что, когда они только начинают; требуется всего один неудачный опыт при попытке обновить свой собственный, некомментированный код (или, что еще хуже: чужой!), чтобы убедить вас в его ценности, хотя 🙂
Спрятать мышь
На данный момент, у вас есть большой тупой курсор мыши, нависающий над вашим аватаром:
Мы можем исправить это с помощью небольшого CSS. Измените свою таблицу стилей следующим образом:
1
2
3
4
|
canvas {
border: 1px solid black;
cursor: none;
}
|
В большинстве браузеров это приведет к исчезновению курсора, когда он находится на вершине холста … но не в Chrome.
(Обновление 18/11/2010: Chrome теперь поддерживает это! В любом случае я оставлю здесь остальную часть текста, иначе все будет очень запутанно.)
Chrome не поддерживает cursor: none;
, но он позволяет заменить курсор на файл PNG по вашему выбору. Таким образом, вы можете создать полностью прозрачный PNG-файл, поместить его в папку img и использовать его следующим образом:
1
2
3
4
|
canvas {
border: 1px solid black;
cursor: url(«../img/transparentcursor.png»), none;
}
|
(Я должен был поставить ../ в начале URL-адреса, потому что в CSS относительные пути относятся к папке файла CSS, а не к файлу HTML, а «..» говорит: «папка над этим one «. Кроме того, я поставил , none
после этого, потому что это означает, что если какие-либо браузеры не поддерживают использование PNG для курсоров, они будут использовать вместо этого атрибут none
. Можете ли вы понять, почему я хотел избежать сосредоточения внимания на перекрестных Совместимость?)
К сожалению, это тоже не работает, потому что если вы используете полностью прозрачный PNG, Chrome просто показывает сплошной черный прямоугольник. Спасибо, Хром.
Вместо этого я сделал PNG размером 1x1px, который почти прозрачен (один пиксель белый, с непрозрачностью 1%). Вы можете скачать его здесь . Скопируйте его в папку img , затем измените таблицу стилей CSS:
1
2
3
4
|
canvas {
border: 1px solid black;
cursor: url(«../img/almosttransparent.png»), none;
}
|
Проверьте это . После всех этих усилий это работает.
Сделать врага
До сих пор мы многого достигли, но нашей игре избегать нечего! Последнее, что мы сделаем в этой части серии, это создадим врага.
Нам нужно изображение, чтобы представить это. Нарисуйте все что угодно, но убедитесь, что оно примерно круглое, размером около 30×30 пикселей. Я собираюсь взять мой сигнал от FrozenHaddock снова. Он выбрал смайлик для врага своей игры; Я не уверен, почему, но я подозреваю, что это был комментарий о распространении смайликов в современном разговоре; тоска по дням эмоций по смайликам, где поэты вкладывают свои сердца в одно предложение текста, а не просто набирают точку с запятой в скобках, как все, что угодно. Или, может быть, это потому, что смайлики легче рисовать. В любом случае, вот мой:
Назовите ваш врага и поместите его в каталог img .
Вы, вероятно, можете понять, как нарисовать этого врага (неподвижного) на холсте — если это так, попробуйте! Еще раз, я рекомендую вам сделать это, прежде чем читать дальше.
Вот мое решение:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
function drawAvatar() { var gameCanvas = document.getElementById( "gameCanvas" ); var avatarImage = new Image(); avatarImage.src = "img/avatar.png" ; gameCanvas.getContext( "2d" ).drawImage(avatarImage, Math.random() * 100, Math.random() * 100); gameCanvas.addEventListener( "mousemove" , redrawAvatar); }
function redrawAvatar(mouseEvent) { var gameCanvas = document.getElementById( "gameCanvas" ); var avatarImage = new Image(); var enemyImage = new Image(); avatarImage.src = "img/avatar.png" ; enemyImage.src = "img/enemy.png" ; gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext( "2d" ).drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY); gameCanvas.getContext( "2d" ).drawImage(enemyImage, 250, 150); }
|
Ваш метод может отличаться от моего — все в порядке, пока он работает! Но для согласованности, пожалуйста, скопируйте мой метод в ваш код.
Обратите внимание, что я держу var
утверждения в верхней части их соответствующих функций. Это не является строго необходимым, но оно поддерживает порядок, поэтому я рекомендую его.
Кроме того, ничто здесь не должно вас удивлять. Там нет особого значения для координат (250, 150), которые я выбрал.
Положить «Избегать» в «Avoider Game»
Мы не будем беспокоиться о том, чтобы заставить противника двигаться в этой части урока; эта тема заслуживает больше места, чем я могу себе здесь позволить. Но мы проверим столкновение аватара с врагом!
Во-первых, мы сделаем что-то попроще: мы рассмотрим определенную область экрана как «запрещенную» и откроем диалоговое окно, если аватар переместится в эту область.
Измените вашу redrawAvatar()
функцию так:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
function redrawAvatar(mouseEvent) { var gameCanvas = document.getElementById( "gameCanvas" ); var avatarImage = new Image(); var enemyImage = new Image(); avatarImage.src = "img/avatar.png" ; enemyImage.src = "img/enemy.png" ; gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext( "2d" ).drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY); gameCanvas.getContext( "2d" ).drawImage(enemyImage, 250, 150); if (mouseEvent.offsetX < 100) { alert( "Too far left!" ); }
}
|
Попробуйте это . Если ваш аватар заходит слишком далеко к левой стороне экрана, появляется диалоговое окно.
Давайте внимательнее посмотрим на код:
1
2
3
|
if (condition) { outcome; }
|
if
Утверждение является способом проверки того , что произошло что — то. Он состоит из двух частей: условия внутри скобок и результата внутри фигурных скобок. Если условие истинно, то результат называется.
В нашем случае условие есть mouseEvent.offsetX < 100
. В <
означает символ «меньше чем», и помните , что mouseEvent.offsetX
это горизонтальное расстояние курсора от левого края холста. Таким образом, это проверяет, находится ли курсор в пределах 100 пикселей от левого края холста. Если это так, то …
… результат запущен. И в нашем случае результатом является alert("Too far left!");
функция диалогового окна, которую мы использовали несколько раз.
Есть смысл? Хорошо, хорошо, потому что я собираюсь сделать это более сложным:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
function redrawAvatar(mouseEvent) { var gameCanvas = document.getElementById( "gameCanvas" ); var avatarImage = new Image(); var enemyImage = new Image(); avatarImage.src = "img/avatar.png" ; enemyImage.src = "img/enemy.png" ; gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext( "2d" ).drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY); gameCanvas.getContext( "2d" ).drawImage(enemyImage, 250, 150); if (mouseEvent.offsetX < 100 || mouseEvent.offsetY < 100) { alert( "Too far left, or too far up!" ); }
}
|
В этом коде мы ввели новый оператор: ||
, ||
означает (и произносится) «или». Поэтому if
утверждение гласит:
«Если мышь находится в пределах 100 пикселей от левого края холста, ИЛИ мышь находится в пределах 100 пикселей от верхнего края холста, отобразите диалоговое окно».
Попробуйте это , и вы увидите, что вы не можете добраться до верхней или левой части холста.
Как насчет этого:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
function redrawAvatar(mouseEvent) { var gameCanvas = document.getElementById( "gameCanvas" ); var avatarImage = new Image(); var enemyImage = new Image(); avatarImage.src = "img/avatar.png" ; enemyImage.src = "img/enemy.png" ; gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext( "2d" ).drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY); gameCanvas.getContext( "2d" ).drawImage(enemyImage, 250, 150); if (mouseEvent.offsetX > 150 && mouseEvent.offsetX < 250) { alert( "Stay out of the middle!" ); }
}
|
В &&
означает оператор (и произносится) «и», а >
значит «больше». Итак, наше if
утверждение теперь гласит:
«Если мышь находится на расстоянии более 150 пикселей от левого края холста, а мышь на расстоянии менее 250 пикселей от левого края холста, отобразите диалоговое окно».
Проверьте это , и вы увидите, что у нас фактически есть невидимая «полоска» по центру холста, где мы не можем поставить нашу мышь.
Мы не ограничены двумя пунктами одновременно; Проверь это:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
function redrawAvatar(mouseEvent) { var gameCanvas = document.getElementById( "gameCanvas" ); var avatarImage = new Image(); var enemyImage = new Image(); avatarImage.src = "img/avatar.png" ; enemyImage.src = "img/enemy.png" ; gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext( "2d" ).drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY); gameCanvas.getContext( "2d" ).drawImage(enemyImage, 250, 150); if (mouseEvent.offsetX > 150 && mouseEvent.offsetX < 250 && mouseEvent.offsetY > 100 && mouseEvent.offsetY < 200) { alert( "Stay out of the center!" ); }
}
|
Теперь мы эффективно нарисовали прямоугольник размером 100×100 пикселей в центре холста, куда мы не можем поместить свою мышь.
Вы видите, куда мы идем с этим?
Вместо поля размером 100×100 пикселей в середине холста мы должны использовать поле размером 30×30 пикселей, расположенное там, где находится наш враг.
Это последнее, что мы собираемся сделать в этой части урока, поэтому еще раз рекомендую вам попробовать самим. Это довольно странно — вам, вероятно, захочется достать немного бумаги, чтобы нарисовать аватар и врага и обозначить некоторые координаты, — но вы можете сделать это, если будете использовать то, что уже изучили.
Мое решение ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
function redrawAvatar(mouseEvent) { var gameCanvas = document.getElementById( "gameCanvas" ); var avatarImage = new Image(); var enemyImage = new Image(); avatarImage.src = "img/avatar.png" ; enemyImage.src = "img/enemy.png" ; gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext( "2d" ).drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY); gameCanvas.getContext( "2d" ).drawImage(enemyImage, 250, 150); //my avatar is 30px wide and the enemy is at x=250, so I have to check whether mouseEvent.offsetX is within 30px //either side of x=250 (ie, from 220 to 280) //similarly, since my avatar is 33px tall, I have to check whether mouseEvent.offsetX is within 33px ABOVE y=150 //but since enemy is only 30px tall, I also check whether mouseEvent.offsetX is within 30px BELOW y=150 //therefore, I check from (117 to 180) if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) { alert( "You hit the enemy!" ); }
}
|
В следующий раз
Вот и все для первой части урока. В следующей части мы приведем в движение этого врага и добавим еще больше, чтобы это стало настоящей игрой!
А пока почему бы не поэкспериментировать с тем, что вы узнали? Вы можете попробовать добавить несколько (неподвижных) врагов самостоятельно, возможно, с другой графикой. Или вы можете добавить несколько аватаров на экран одновременно, следуя за мышью разными способами. Что произойдет, если вы используете что-то вроде mouseEvent.offsetX + 100
или 300 - mouseEvent.offsetY
в вашем звонке gameCanvas.getContext("2d").drawImage()
?
Я надеюсь, вам понравилось это до сих пор. Если что-то не понятно, пожалуйста, спросите об этом в комментариях 🙂
(Еще одно быстрое замечание: на данный момент ваш код должен нормально работать на вашем компьютере, но не будет работать при загрузке в Интернет. Мои демонстрационные ролики работают в Интернете, потому что я сделал хитрый трюк. Не волнуйтесь, я Я объясню, как решить эту проблему в следующей части урока.)