Сеть движется быстро — настолько быстро, что наш оригинальный учебник по 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. Спасибо, что нашли время, чтобы прочитать этот учебник.