Сеть движется быстро — настолько быстро, что наш оригинальный учебник по EaselJS уже устарел! В этом руководстве вы узнаете, как использовать новейший набор CreateJS , создав простой клон Pong.
Окончательный результат предварительного просмотра
Давайте посмотрим на конечный результат, к которому мы будем стремиться:
Это руководство основано на книге Карлоса Янеза « Создайте игру в понг в HTML5 с EaselJS» , которая, в свою очередь, основана на руководстве « Начало работы с EaselJS» . Графика и звуковые эффекты взяты из предыдущего урока.
Шаг 1: Создайте index.html
Это будет наш основной файл index.html :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
<!DOCTYPE html>
<html>
<head>
<title>Pong</title>
<style>/* Removes Mobile Highlight */ *{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style>
<script src=»http://code.createjs.com/easeljs-0.4.2.min.js»></script>
<script src=»http://code.createjs.com/tweenjs-0.2.0.min.js»></script>
<script src=»http://code.createjs.com/soundjs-0.2.0.min.js»></script>
<script src=»http://code.createjs.com/preloadjs-0.1.0.min.js»></script>
<script src=»http://code.createjs.com/movieclip-0.4.1.min.js»></script>
<script src=»assets/soundjs.flashplugin-0.2.0.min.js»></script>
<script src=»Main.js»></script>
</head>
<body onload=»Main();»>
<canvas id=»PongStage» width=»480″ height=»320″></canvas>
</body>
</html>
|
Как видите, он довольно короткий и состоит в основном из загрузки библиотек CreateJS .
После выпуска CreateJS (который в основном объединяет все отдельные библиотеки EaselJS) нам больше не нужно загружать файлы JS и размещать их на нашем сайте; файлы теперь помещаются в CDN (сеть доставки контента), что позволяет нам загружать эти файлы удаленно как можно быстрее.
Давайте рассмотрим код:
|
1
|
<style>/* Removes Mobile Highlight */ *{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style>
|
Эта строка удаляет выделение мобильного телефона, которое может появиться, когда вы пытаетесь играть в игру на мобильном телефоне. (Мобильное выделение вызывает выделение объекта canvas и, таким образом, игнорирует ваши движения пальцев.)
Далее у нас есть загрузка библиотек CreateJS:>
|
1
2
3
4
5
|
<script src=»http://code.createjs.com/easeljs-0.4.2.min.js»></script>
<script src=»http://code.createjs.com/tweenjs-0.2.0.min.js»></script>
<script src=»http://code.createjs.com/soundjs-0.2.0.min.js»></script>
<script src=»http://code.createjs.com/preloadjs-0.1.0.min.js»></script>
<script src=»http://code.createjs.com/movieclip-0.4.1.min.js»></script>
|
Этот код загружает файлы JS из CDN CreateJS и в основном позволяет нам использовать любые функции CreateJS в нашем коде
Затем мы загрузим плагин SoundJS Flash, который обеспечивает поддержку звука для браузеров, которые не поддерживают HTML5 Audio. Это делается с помощью SWF (объект Flash) для загрузки звуков.
|
1
|
<script src=»assets/soundjs.flashplugin-0.2.0.min.js»></script>
|
В этом случае мы не будем использовать CDN; вместо этого мы загрузим библиотеку SoundJS с http://createjs.com/#!/SoundJS/download и FlashAudioPlugin.swf файлы soundjs.flashplugin-0.2.0.min.js и FlashAudioPlugin.swf в локальную папку с именем assets .
Последний из файлов JS, мы Main.js файл Main.js который будет содержать весь код нашей игры:
|
1
|
<script src=»Main.js»></script>
|
Наконец, давайте разместим объект Canvas на нашей сцене.
|
1
2
3
|
<body onload=»Main();»>
<canvas id=»PongStage» width=»480″ height=»320″></canvas>
</body>
|
Теперь мы можем начать работать над кодом игры.
Шаг 2: Переменные
Наш игровой код будет внутри файла с именем Main.js , поэтому создайте и сохраните его сейчас.
Прежде всего, давайте определим переменные для всех графических объектов в игре:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
var canvas;
var stage;
// Graphics
//[Background]
var bg;
//[Title View]
var main;
var startB;
var creditsB;
//[Credits]
var credits;
//[Game View]
var player;
var ball;
var cpu;
var win;
var lose;
|
Я добавил комментарий для каждой переменной, чтобы вы знали, что мы будем загружать в эту переменную
Далее, оценки:
|
1
2
3
4
5
|
//[Score]
var playerScore;
var cpuScore;
var cpuSpeed=6;
|
Нам понадобятся переменные для скорости мяча:
|
1
2
3
4
|
// Variables
var xSpeed = 5;
var ySpeed = 5;
|
Вы можете изменить эти значения на любое другое, если хотите сделать игру проще или сложнее.
Если вы разработчик Flash, вы знаете, что onEnterFrame Flash очень полезен при создании игр, поскольку в каждом конкретном кадре должно происходить что-то. (Если вы не знакомы с этой идеей, прочитайте эту статью в Game Loop .)
У нас есть эквивалент для onEnterFrame в CreateJS, и это объект ticker , который может запускать код каждую долю секунды. Давайте создадим переменную, которая будет ссылаться на нее:
|
1
|
var tkr = new Object;
|
Далее у нас есть preloader, который будет использовать новые методы PreloadJS.
|
1
2
3
4
|
//preloader
var preloader;
var manifest;
var totalLoaded = 0;
|
-
preloader— будет содержать объект PreloadJS. -
manifest— будет содержать список файлов, которые нам нужно загрузить. -
totalLoaded— эта переменная будет содержать количество уже загруженных файлов.
И последнее, но не менее важное в нашем списке переменных, у нас есть TitleView , который будет содержать несколько графических TitleView для их совместного отображения (например, Flash DisplayObjectContainer ).
|
1
|
var TitleView = new Container();
|
Давайте перейдем к основной функции …
Шаг 3: Основная () функция
Эта функция является первой функцией, которая запускается после загрузки всех файлов JS из index.html . Но как называется эта функция?
Хорошо, помните эту строку из файла index.html ?
|
1
|
<body onload=»Main();»>
|
В этом фрагменте кода говорится, что после загрузки HTML (и библиотек JS) должна быть запущена функция Main .
Давайте рассмотрим это:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
function Main()
{
/* Link Canvas */
canvas = document.getElementById(‘PongStage’);
stage = new Stage(canvas);
stage.mouseEventsEnabled = true;
/* Set The Flash Plugin for browsers that don’t support SoundJS */
SoundJS.FlashPlugin.BASE_PATH = «assets/»;
if (!SoundJS.checkPlugin(true)) {
alert(«Error!»);
return;
}
manifest = [
{src:»bg.png», id:»bg»},
{src:»main.png», id:»main»},
{src:»startB.png», id:»startB»},
{src:»creditsB.png», id:»creditsB»},
{src:»credits.png», id:»credits»},
{src:»paddle.png», id:»cpu»},
{src:»paddle.png», id:»player»},
{src:»ball.png», id:»ball»},
{src:»win.png», id:»win»},
{src:»lose.png», id:»lose»},
{src:»playerScore.mp3|playerScore.ogg», id:»playerScore»},
{src:»enemyScore.mp3|enemyScore.ogg», id:»enemyScore»},
{src:»hit.mp3|hit.ogg», id:»hit»},
{src:»wall.mp3|wall.ogg», id:»wall»}
];
preloader = new PreloadJS();
preloader.installPlugin(SoundJS);
preloader.onProgress = handleProgress;
preloader.onComplete = handleComplete;
preloader.onFileLoad = handleFileLoad;
preloader.loadManifest(manifest);
/* Ticker */
Ticker.setFPS(30);
Ticker.addListener(stage);
}
|
Давайте разберем каждую часть:
|
1
2
3
4
|
canvas = document.getElementById(‘PongStage’);
stage = new Stage(canvas);
stage.mouseEventsEnabled = true;
|
Здесь мы связываем объект PongStage Canvas из файла index.html с переменной canvas, а затем создаем объект Stage из этого холста. (Этап позволит нам разместить на нем предметы.)
mouseEventsEnabled позволяет нам использовать события мыши, чтобы мы могли определять движения мыши и щелчки.
|
1
2
3
4
5
6
|
/* Set The Flash Plugin for browsers that don’t support SoundJS */
SoundJS.FlashPlugin.BASE_PATH = «assets/»;
if (!SoundJS.checkPlugin(true)) {
alert(«Error!»);
return;
}
|
Здесь мы настраиваем расположение плагина Flash sound для тех браузеров, в которых HTML5 Audio не поддерживается
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
manifest = [
{src:»bg.png», id:»bg»},
{src:»main.png», id:»main»},
{src:»startB.png», id:»startB»},
{src:»creditsB.png», id:»creditsB»},
{src:»credits.png», id:»credits»},
{src:»paddle.png», id:»cpu»},
{src:»paddle.png», id:»player»},
{src:»ball.png», id:»ball»},
{src:»win.png», id:»win»},
{src:»lose.png», id:»lose»},
{src:»playerScore.mp3|playerScore.ogg», id:»playerScore»},
{src:»enemyScore.mp3|enemyScore.ogg», id:»enemyScore»},
{src:»hit.mp3|hit.ogg», id:»hit»},
{src:»wall.mp3|wall.ogg», id:»wall»}
];
|
В переменную manifest мы помещаем массив файлов, которые мы хотим загрузить (и предоставляем уникальный идентификатор для каждого). Каждый звук имеет два формата — MP3 и OGG — потому что разные браузеры (не) совместимы с разными форматами.
|
1
2
3
4
5
6
|
preloader = new PreloadJS();
preloader.installPlugin(SoundJS);
preloader.onProgress = handleProgress;
preloader.onComplete = handleComplete;
preloader.onFileLoad = handleFileLoad;
preloader.loadManifest(manifest);
|
Здесь мы настраиваем объект preloader, используя PreloadJS. PreloadJS является новым дополнением к библиотекам CreateJS и довольно полезным.
Мы создаем новый объект PreloadJS и помещаем его в переменную preloader, затем присваиваем метод каждому событию ( onProgress , onComplete , onFileLoad ). Наконец, мы используем preloader для загрузки манифеста, который мы создали ранее.
|
1
2
|
Ticker.setFPS(30);
Ticker.addListener(stage);
|
Здесь мы добавляем объект Ticker на сцену и устанавливаем частоту кадров 30 FPS; мы будем использовать его позже в игре для функциональности enterFrame .
Шаг 4: Создание функций Preloader
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
function handleProgress(event)
{
//use event.loaded to get the percentage of the loading
}
function handleComplete(event) {
//triggered when all loading is complete
}
function handleFileLoad(event) {
//triggered when an individual file completes loading
switch(event.type)
{
case PreloadJS.IMAGE:
//image loaded
var img = new Image();
img.src = event.src;
img.onload = handleLoadComplete;
window[event.id] = new Bitmap(img);
break;
case PreloadJS.SOUND:
//sound loaded
handleLoadComplete();
break;
}
}
|
Давайте рассмотрим функции:
-
handleProgress— в этой функции вы сможете отслеживать процент загрузки, используя этот параметр:event.loaded. Вы можете использовать это, чтобы создать, например, индикатор выполнения. -
handleComplete— эта функция вызывается после загрузки всех файлов (на случай, если вы хотите что-то там разместить). -
handleFileLoad— поскольку мы загружаем два типа файлов — изображения и звуки — у нас есть эта функция, которая обрабатывает каждый из них в отдельности. Если это изображение, мы создаем растровое изображение и помещаем его в переменную (имя которой совпадает с идентификатором загруженного изображения), а затем вызываем функциюhandleLoadComplete(которую мы напишем далее); если это звук, тогда мы просто вызываемhandleLoadCompleteнемедленно.
Теперь давайте обсудим функцию handleLoadComplete я только что упомянул:
|
01
02
03
04
05
06
07
08
09
10
|
function handleLoadComplete(event)
{
totalLoaded++;
if(manifest.length==totalLoaded)
{
addTitleView();
}
}
|
Это довольно простая функция; мы увеличиваем переменную totalLoaded (которая содержит количество загруженных ресурсов), а затем проверяем, совпадает ли количество элементов в нашем манифесте с количеством загруженных ресурсов, и, если это так, переходим на экран главного меню.
Шаг 5: Создание главного меню

|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
function addTitleView()
{
//console.log(«Add Title View»);
startB.x = 240 — 31.5;
startB.y = 160;
startB.name = ‘startB’;
creditsB.x = 241 — 42;
creditsB.y = 200;
TitleView.addChild(main, startB, creditsB);
stage.addChild(bg, TitleView);
stage.update();
// Button Listeners
startB.onPress = tweenTitleView;
creditsB.onPress = showCredits;
|
Здесь нет ничего особенного. Мы onPress изображения фона, кнопки «Пуск» и «Кредиты» на сцену и связываем обработчики событий onPress с кнопками «Пуск» и «Кредиты».
Вот функции, которые отображают и удаляют экран кредитов и tweenTitleView который запускает игру:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
function showCredits()
{
// Show Credits
credits.x = 480;
stage.addChild(credits);
stage.update();
Tween.get(credits).to({x:0}, 300);
credits.onPress = hideCredits;
}
// Hide Credits
function hideCredits(e)
{
Tween.get(credits).to({x:480}, 300).call(rmvCredits);
}
// Remove Credits
function rmvCredits()
{
stage.removeChild(credits);
}
// Tween Title View
function tweenTitleView()
{
// Start Game
Tween.get(TitleView).to({y:-320}, 300).call(addGameView);
}
|
Шаг 6: Код игры

Мы достигли основной части этого урока, которая представляет собой код самой игры.
Прежде всего, нам нужно добавить все необходимые активы на сцену, поэтому мы делаем это в функции addGameView :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
function addGameView()
{
// Destroy Menu & Credits screen
stage.removeChild(TitleView);
TitleView = null;
credits = null;
// Add Game View
player.x = 2;
player.y = 160 — 37.5;
cpu.x = 480 — 25;
cpu.y = 160 — 37.5;
ball.x = 240 — 15;
ball.y = 160 — 15;
// Score
playerScore = new Text(‘0’, ‘bold 20px Arial’, ‘#A3FF24’);
playerScore.x = 211;
playerScore.y = 20;
cpuScore = new Text(‘0’, ‘bold 20px Arial’, ‘#A3FF24’);
cpuScore.x = 262;
cpuScore.y = 20;
stage.addChild(playerScore, cpuScore, player, cpu, ball);
stage.update();
// Start Listener
bg.onPress = startGame;
}
|
Опять же, довольно простая функция, которая помещает объекты на экран и добавляет mouseEvent к фоновому изображению, так что, когда пользователь щелкает по нему, игра запускается (мы будем вызывать функцию startGame ).
Давайте рассмотрим функцию startGame :
|
1
2
3
4
5
6
7
8
|
function startGame(e)
{
bg.onPress = null;
stage.onMouseMove = movePaddle;
Ticker.addListener(tkr, false);
tkr.tick = update;
}
|
Здесь, как вы можете видеть, помимо добавления события onMouseMove , которое переместит наш манипулятор. Мы добавляем tick событие, которое будет вызывать функцию update в каждом кадре.
Давайте рассмотрим функции movePaddle и reset :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
function movePaddle(e)
{
// Mouse Movement
player.y = e.stageY;
}
/* Reset */
function reset()
{
ball.x = 240 — 15;
ball.y = 160 — 15;
player.y = 160 — 37.5;
cpu.y = 160 — 37.5;
stage.onMouseMove = null;
Ticker.removeListener(tkr);
bg.onPress = startGame;
}
|
В movePaddle мы в основном movePaddle весло пользователя в y-координату мыши.
При reset мы делаем нечто похожее на addGameView , за исключением того, что здесь мы не добавляем никаких графических элементов, поскольку они уже находятся на экране.
Используя функцию alert мы отобразим всплывающее окно с выигрышем и проигрышем:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
function alert(e)
{
Ticker.removeListener(tkr);
stage.onMouseMove = null;
bg.onPress = null
if(e == ‘win’)
{
win.x = 140;
win.y = -90;
stage.addChild(win);
Tween.get(win).to({y: 115}, 300);
}
else
{
lose.x = 140;
lose.y = -90;
stage.addChild(lose);
Tween.get(lose).to({y: 115}, 300);
}
}
|
Шаг 7: игровой цикл
Теперь, для последней части нашего урока, мы будем работать над функцией update (которая происходит в каждом кадре игры — по аналогии с onEnterFrame во Flash):
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
function update()
{
// Ball Movement
ball.x = ball.x + xSpeed;
ball.y = ball.y + ySpeed;
// Cpu Movement
if(cpu.y < ball.y) {
cpu.y = cpu.y + 4;
}
else if(cpu.y > ball.y) {
cpu.y = cpu.y — 4;
}
// Wall Collision
if((ball.y) < 0) { ySpeed = -ySpeed;
if((ball.y + (30)) > 320) { ySpeed = -ySpeed;
/* CPU Score */
if((ball.x) < 0)
{
xSpeed = -xSpeed;
cpuScore.text = parseInt(cpuScore.text + 1);
reset();
SoundJS.play(‘enemyScore’);
}
/* Player Score */
if((ball.x + (30)) > 480)
{
xSpeed = -xSpeed;
playerScore.text = parseInt(playerScore.text + 1);
reset();
SoundJS.play(‘playerScore’);
}
/* Cpu collision */
if(ball.x + 30 > cpu.x && ball.x + 30 < cpu.x + 22 && ball.y >= cpu.y && ball.y < cpu.y + 75)
{
xSpeed *= -1;
SoundJS.play(‘hit’);
}
/* Player collision */
if(ball.x <= player.x + 22 && ball.x > player.x && ball.y >= player.y && ball.y < player.y + 75)
{
xSpeed *= -1;
SoundJS.play(‘hit’);
}
/* Stop Paddle from going out of canvas */
if(player.y >= 249)
{
player.y = 249;
}
/* Check for Win */
if(playerScore.text == ’10’)
{
alert(‘win’);
}
/* Check for Game Over */
if(cpuScore.text == ’10’)
{
alert(‘lose’);
}
}
|
Выглядит страшно, не так ли? Не волнуйтесь, мы рассмотрим каждую часть и обсудим это.
|
1
2
3
4
|
// Ball Movement
ball.x = ball.x + xSpeed;
ball.y = ball.y + ySpeed;
|
В каждом кадре шар будет двигаться в соответствии со своими значениями скорости х и у
|
1
2
3
4
5
6
7
8
|
// Cpu Movement
if((cpu.y+32) < (ball.y-14)) {
cpu.y = cpu.y + cpuSpeed;
}
else if((cpu.y+32) > (ball.y+14)) {
cpu.y = cpu.y — cpuSpeed;
}
|
Здесь у нас есть базовый ИИ компьютера, в котором весло компьютера просто следует за мячом без какой-либо специальной логики. Мы просто сравниваем расположение центра весла (именно поэтому мы добавляем 32 пикселя к значению процессора Y) с местоположением мяча с небольшим смещением и перемещаем весло вверх или вниз по мере необходимости.
|
1
2
3
4
5
6
7
8
|
if((ball.y) < 0) { //top
ySpeed = -ySpeed;
SoundJS.play(‘wall’);
};
if((ball.y + (30)) > 320) { //bottom
ySpeed = -ySpeed;
SoundJS.play(‘wall’);
};
|
Если мяч попадает в верхнюю или нижнюю границу экрана, шар меняет направление, и мы играем звук Wall Hit.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
/* CPU Score */
if((ball.x) < 0)
{
xSpeed = -xSpeed;
cpuScore.text = parseInt(cpuScore.text + 1);
reset();
SoundJS.play(‘enemyScore’);
}
/* Player Score */
if((ball.x + (30)) > 480)
{
xSpeed = -xSpeed;
playerScore.text = parseInt(playerScore.text + 1);
reset();
SoundJS.play(‘playerScore’);
}
|
Регистрация счета проста: если мяч проходит левую или правую границу, это увеличивает счет игрока или процессора соответственно, воспроизводит звук и сбрасывает местоположение объектов, используя функцию reset мы обсуждали ранее.
|
01
02
03
04
05
06
07
08
09
10
11
12
|
/* CPU collision */
if(ball.x + 30 > cpu.x && ball.x + 30 < cpu.x + 22 && ball.y >= cpu.y && ball.y < cpu.y + 75)
{
xSpeed *= -1;
SoundJS.play(‘hit’);
}
/* Player collision */
if(ball.x <= player.x + 22 && ball.x > player.x && ball.y >= player.y && ball.y < player.y + 75)
{
xSpeed *= -1;
SoundJS.play(‘hit’);
}
|
Здесь мы имеем дело со столкновениями мяча с веслами; каждый раз, когда мяч ударяет по одной из лопастей, мяч меняет направление и воспроизводится звук
|
1
2
3
4
|
if(player.y >= 249)
{
player.y = 249;
}
|
Если весло игрока выходит за пределы, мы помещаем его обратно в пределы.
|
01
02
03
04
05
06
07
08
09
10
|
/* Check for Win */
if(playerScore.text == ’10’)
{
alert(‘win’);
}
/* Check for Game Over */
if(cpuScore.text == ’10’)
{
alert(‘lose’);
}
|
В этом фрагменте мы проверяем, достиг ли счет любого из игроков 10 баллов, и если да, то отображаем всплывающее окно проигравшего или проигравшего игрока (в зависимости от его статуса победителя).
Вывод
Вот и все, вы создали целую игру понг с использованием CreateJS. Спасибо, что нашли время, чтобы прочитать этот учебник.
