Статьи

Изучите CreateJS, создав игру для HTML5 Pong

Сеть движется быстро — настолько быстро, что наш оригинальный учебник по EaselJS уже устарел! В этом руководстве вы узнаете, как использовать новейший набор CreateJS , создав простой клон Pong.


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

Игра понг

Нажмите, чтобы играть

Это руководство основано на книге Карлоса Янеза « Создайте игру в понг в HTML5 с EaselJS» , которая, в свою очередь, основана на руководстве « Начало работы с EaselJS» . Графика и звуковые эффекты взяты из предыдущего урока.


Это будет наш основной файл 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>

Теперь мы можем начать работать над кодом игры.


Наш игровой код будет внутри файла с именем 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();

Давайте перейдем к основной функции …


Эта функция является первой функцией, которая запускается после загрузки всех файлов 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 .


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 (которая содержит количество загруженных ресурсов), а затем проверяем, совпадает ли количество элементов в нашем манифесте с количеством загруженных ресурсов, и, если это так, переходим на экран главного меню.


Главное меню
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);
}

Игра понг

Мы достигли основной части этого урока, которая представляет собой код самой игры.

Прежде всего, нам нужно добавить все необходимые активы на сцену, поэтому мы делаем это в функции 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);
    }
}

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