Створення сайд-скролера с паралакс-ефектом на основі Pixi.js. Частина 1

21 хв. читання

Якщо ви колись грали в Canabalt чи Monster Dash, то можливо вам цікаво, як побудовані ігрові мапи в цих іграшках. В цій статті ми зробимо перші кроки до того, щоб зробити схожий скролер засобами JS та грального рушія Pixi.js

Чого ми навчимося

  • Основи Pixi.js;
  • Як працювати з текстурами та спрайтами;
  • Створимо скромний скролер з паралакс-ефектом.

Які навички необхідні

  • Базове розуміння JavaScript та ActionScript

JavaScript заполонив усе. Завдяки невтомному розвитку браузерів та постійно зростаючий кількості бібліотек для JS, ми дійсно починаємо бачити розквіт ери розробки HTML5-ігор. Але така кількість бібліотек створює нову проблему - проблему вибору. Ця серія статей ознайомить вас з основами розробки ігор з використанням JavaScript і зосередиться на одній з самих популярних бібліотек, яка спрощує це заняття -- Pixi.js. Pixi.js -- новий фреймворк для 2D рендерінгу засобами WebGL та HTML5 Canvas. Наприкінці цього керівництва ми побудуємо таку ігрову мапу з паралакс-ефектом:

Клацніть, щоб запустити демо

Якщо ви клацнули зображення, що вище, то ви побачите фінальну версію, яку ми будемо будувати впродовж серії статей. Зауважте, що мапа складається з трьох шарів: найдальший, середній та передній. Аби зосередитися на головних основах паралаксу, у першій частині цього туторіала ми будемо працювати лише з двома шарами: найдальшим та середнім. І, звісно, покриємо необхідні основи Pixі.js. Також, якщо ви новачок у JavaScript-програмуванні, то це чудове місце, аби почати знайомитись з розробкою ігор на HTML5.

Клацніть, щоб запустити демо

Перед тим як почати, давайте все ж подивимося на те, що ми зробимо конкретно в цій частині. Для цього клацніть на зображення, що вище. Тут ви можете завантажити сирцевий код.

У самому початку

Перед тим, як почати кодити, вам знадобиться слушний редактор тексту чи IDE. Я би порадив вам використовувати Sublime Text 2. Вам також потрібен сучасний браузер, у якому тестувати результати. Пропоную Google Chrome. Деякі функції його інструментарію розробника знадобляться нам на протязі цього уроку. Також, вам потрібен налаштований на комп'ютері web-server. Користувачі Windows можуть скористатися послугами Microsoft: налаштування першого IIS веб-вузлу, коли користувачі Mac OS X можуть спробувати налаштувати вбудований Apache, юзери Mountain Lion можуть прочитати про налаштування Apache на їхній системі тут.

Якщо ви щасливий володар власного хостингу, то, звісно, ви вільні тестувати ваші результати на ньому. Ще одна альтернатива -- сервіс DropPages, та акаунт DropBox.

Коли налаштування сервера закінчено, створіть у кореневій папці нову теку і назвіть "parallax-scroller". Якщо ви користувач Windows, то шлях до папки з нашою грою має бути "C:\inetpub\parallax-scroller" або "/Users/your_user_name/Sites" з вашим юзернеймом, якщо у вас Mac OS X. Ще дещо -- Pixi.js, він розміщений на Github. Завантажте його, клацнувши на "Download Zip", та розпакуйте вміст у папку "parallax- scroller".

В середині теки під назвою "pixi.js-master" знайдіть підпапку "bin". Вона вміщує в собі два JavaScript-файла: "pixi.js" та pixi."dev.js". Вони повністю ідентичні за виключенням того, що перший використовується на етапі релізу, коли другий -- під час розробки, бо про пропонує більш розширені можливості звіту про помилки. На протязі цього та наступних туторіалів ми будемо користуватися "pixi.dev.js"

Ну, і нарешті, нам знадобляться декілька графічних наборів. Навряд чи ви створили свій власний, тому можете взяти готовий ZIP-файл тут, розархівуйте його в теку "parallax-scroller". В Windows папка "parallax-scroller" зараз має мати ось такий вигляд:

Windows. Структура папки

Ось такий в Mac OS X:

Створення сайд-скролера с паралакс-ефектом на основі Pixi.js. Частина 1

Наразі ми вже можемо починати, запускайте ваш редактор.

Canvas

Кожен проект на Pixi.js починається з HTML-файлу, куди ми під'єднаємо pixi- бібліотеку та створимо canvas-елемент -- область, де буде відмальовуватись наша гра. Створіть новий файл, назвіть його "index.html" та зробіть його вміст таким:

        <meta charset="UTF-8">
        <title>Parallax Scrolling Demo</title>

Тут все гранично просто -- найпростіший HTML-документ з "" та "" елементами. Тепер додамо сюди canvas. Для цього треба вставити в "" наступне:

      <div>
        <canvas height="384" id="game-canvas" width="512"></canvas>
      </div>

Ми створили область відмальовування вистою в 384px та довжиною в 512px. Зауважте, що ми йому також дали id -- "game-canvas". Це допоможе нам вказати Pixi.js використовувати безпосередньо цей canvas-елемент. Якщо ми зараз відкриємо наш index.html в браузері, то нічого не побачимо, тому що колір canvas'у та фону сторінки однакові. Щоб додати контрасту, та візуально виділити область нашої гри, додамо в голову нашої сторінки відповідний стиль:

        <meta charset="UTF-8">
        <title>Endless Runner Game Demo</title>
        <style>
          body { background-color: #000000; }
          canvas { background-color: #222222; }
        </style>

Результат можна побачити після перезавантаження сторінки.

Під'єднання Pixi.js

Настав час долучити pixi.js до нашого проекту. Викличемо його наприкінці "":

      <div>
        <canvas height="384" id="game-canvas" width="512"></canvas>
      </div>
      <script src="pixi.js-master/bin/pixi.dev.js"></script>

Зверніть увагу, що ми вказуємо шлях до бібліотеки відносно. Перезавантажимо нашу сторінку і подивимось звіт про помилки в інструментах для розробника ("F12" або "Cmd + Opt + i" для Chrome). Перевірте чи підключилась бібліотека, переконавшись у відсутності схожих надписів: x GET file:///Users/ccaleb/Documents/javascript/tutorial/part1/parallax- scroller/pixi.js-master/bin/pixie.js index.html:14

Додамо головну точку входу.

Аби бути переконаним, що наш код почне виконуватись, коли HMTL-документ та бібліотека Pixi.js повністю завантажаться, додамо виклик нашої головної функції за подією "onload":

      <div>
        <canvas height="384" id="game-canvas" width="512"></canvas>
      </div>
      <script src="pixi.js-master/bin/pixi.dev.js"></script>

Тепер додамо саму функцію init(), та зробимо невеличку перевірку:

      <div>
        <canvas height="384" id="game-canvas" width="512"></canvas>
      </div>
      <script src="pixi.js-master/bin/pixi.dev.js"></script>
      <script>
        function init() {
          console.log("init() successfully called.");
        }
      </script>

У консолі має з'явитись такий рядок:

`> init() successfully called.`

Зараз наша функциія init() нічого не робить, зрештою вона буде відповідальна за ініціалізацію та старт нашого скролера.

Ініціалізація pixi.js

Тепер коли у нас є головна точка входу у вигляді функції init(), підемо далі та ініціалізуємо pixi.js. Це робиться в два кроки:

  • створення сцени (stage);
  • вибір типу рендерера (візуалізатора).

Якщо ви колись займались розробкою ігор на Flash, то концепція організації сцени вам буде знайома. Сцена є місцем, де відображаються весь ігровий графічний контент. Рендерер відмальовує сцену, тобто робить її видимою для гравця.

Створимо нову сцену та пов'яжемо її з глобальною змінною "**stage**"

    function init() {
      //console.log("init() successfully called.");		
      stage = new PIXI.Stage(0x66FF99);
    }

API pixi.js являє собою певну кількість класів та методів, які надаються модулем PIXI. PIXI.Stage -- клас, який використовується для створення нових сцен, його конструктор передбачає лише один аргумент -- колір фону. Зауважте, колір, який ми задаємо для нашої сцени, ніяк не пов'язаний з кольором, який вказано для елемента canvas у будь-яких інших місцях.

Після того, як ми створили сцену, вкажемо тип візуалізатора -- PIXI.WebGLRenderer або PIXI.CanvasRenderer. Ми можемо зробити це вручну, але більш універсальним рішенням буде наказати pixi вибирати тип рендерера автоматично згідно до технологій, які підтримує платформа, де нашу гру буде запущено. За замовчуванням, pixi використовує WebGL-візуалізатор, але якщо він з тієї чи іншої причини недоступний, візуалізація буде здійснюватися за допомогою HTML5-canvas. Додамо PIXI.autoDetectRenderer() в наш код:

    function init() {		
      stage = new PIXI.Stage(0x66FF99);
      renderer = PIXI.autoDetectRenderer(
        512,
        384,
        document.getElementById("game-canvas")
      );
    }

Метод PIXI.autoDetectRenderer() вимагає зазначення геометричних параметрів області відмальовування та, звісно, ім'я самого canvas'у в документі. Після того, як метод буде виконано, у глобальній змінній rendrer буде зберігатися назва діючого методу візуалізації: PIXI.WebGLRenderer або PIXI.CanvasRenderer.

Рендерінг

Перезавантажте сторінку. Ми задали lime-green колір для нашохї сцени, але чому ми не бачимо цього? Тому що ми доки не проінструктували візуалізатор, як що- небудь відмальовувати. Це просто виправити, викликавши renderer() у нашій програмі, вказавши, що сами він повинен відображати:

    function init() {		
      stage = new PIXI.Stage(0x66FF99);
      renderer = PIXI.autoDetectRenderer(
        512,
        384,
        document.getElementById("game-canvas")
      );
      renderer.render(stage);
    }

Якщо тепер перезавантажити сторінку, то можна побачити прямокутник зеленого кольору. Все йде за планом.

Додамо дечого на нашу сцену

Зеленого прямокутника для нас замало, тому додамо деякі елементи на нашу сцену. Структура елементів сцени має деревоподібний вигляд, відомий як список відображення. Усе, що додано на сцену, буде відображено візуалізатором. Відмальовка проводиться шарами, залежно від індексу глибини конкретного елемента, тому один може перекривати інший або ховатися позаду нього.

Ми додамо декілька елементів на нашу сцену, один з них буде типу PIXI.Sprite, призначений для малювання зображень.

Враховуючи те, що ми тут намагаємось створити скролер з паралакс ефектом, давайте спробуємо додати зображення, яке буде представляти найдальший задній шар, -- BG-far.png, яке ви знайдете у папці ресурсів:

    function init() {		
      stage = new PIXI.Stage(0x66FF99);
      renderer = PIXI.autoDetectRenderer(
        512,
        384,
        document.getElementById("game-canvas")
      );
    
      var farTexture = PIXI.Texture.fromImage("resources/bg-far.png");
    
      renderer.render(stage);
    }

Після того, як зображення завантажуються pixi, вони зберігаються у вигляді текстур та можуть бути використані безліч разів у безлічі ситуацій. У коді, що вище, ми використали статичний метод PIXI.Texture.fromImage() для того, щоб створити об'єкт класу PIXI.Texture з завантаженим в нього файлом зображення bg-far.png в, також ми дали нашому об'єкту ім'я --farTexture.

Тепер давайте створимо спрайт, вкажемо йому текстуру, та розмістимо його у верхній лівий частині:

    function init() {		
      stage = new PIXI.Stage(0x66FF99);
      renderer = PIXI.autoDetectRenderer(
        512,
        384,
        document.getElementById("game-canvas")
      );
    
      var farTexture = PIXI.Texture.fromImage("resources/bg-far.png");
      far = new PIXI.Sprite(farTexture);
      far.position.x = 0;
      far.position.y = 0;
    
      renderer.render(stage);
    }

Клас PIXI.Sprite потрібен для того, що створювати нові спрайти, він приймає єдиний аргумент -- текстуру, яку майбутній спрайт повинен відображати. Ми назвали наш спрайт far. Зауважте, що відлік координат починається з верхнього лівого краю області відмальовки, тому ми вказали саме такі параметри position.x та position.y, також не буде помилкою така конструкція: position(0, 0).

Спрайт можна обертати навколо точки опори або вісі обертання (pivot point).

Нарешті залишилося додати наш спрайт на сцену, для цього скористаємося методом AddChild() для класу PIXI.Sprite. Зробимо це:

     var farTexture = PIXI.Texture.fromImage("resources/bg-far.png");
      far = new PIXI.Sprite(farTexture);
      far.position.x = 0;
      far.position.y = 0;
      stage.addChild(far);
    
      renderer.render(stage);
    }

Перезавантажте сторінку та подивіться на результат. Важливо зазначити те, що ми бачимо зміни, тому що додали спрайт до того, як ініціалізували rendrer(), якби ми зробили навпаки, то як і раніше бачили лише зелений прямокутник.

Підемо далі та створимо середній шар.

      var farTexture = PIXI.Texture.fromImage("resources/bg-far.png");
      far = new PIXI.Sprite(farTexture);
      far.position.x = 0;
      far.position.y = 0;
      stage.addChild(far);
    
      var midTexture = PIXI.Texture.fromImage("resources/bg-mid.png");
      mid = new PIXI.Sprite(midTexture);
      mid.position.x = 0;
      mid.position.y = 128;
      stage.addChild(mid);
    
      renderer.render(stage);
    }

Перезавантажте знову. Ви мабуть помітили, що середній шар відмальовується поверх найдальшого. Це тому що він доданий пізніше, такий принцип роботи методу AddChild(). Ми також опустили середній шар на 128 пікселей нижче за найдальший -- mid.position.y = 128.

Пам'ятайте що у цій частині ми зосереджуємся лише на двох шарах.

Основний цикл

Відколи у нас є два повноцінних шари, ми можемо приступати до реалізації паралакс-ефектів. Щоб уникнути сумнівів, давайте швидко з'ясуємо, що ми маємо на увазі, коли говоримо "паралакс". Це метод прокрутки, який використовується у відеоіграх, коли шари прокручуються з різною швидкістю, задля створення ілюзії глибини в 2D-іграх і забезпечення додаткового почуття занурення для гравця.

Враховуючи це, ми повинні змусити фоновий шар рухатись повільніше, ніж середній. Тому ми створимо головний цикл, у якому буде змінюватись положення наших шарів, та будемо відмальовувати зміни за допомогою JavaScript-функції requestAnimFrame(), яка визначає оптимальну частоту кадрів для браузера, а потім викликає зазначену функцію, коли область рендерингу може бути перевідмальована:

      var midTexture = PIXI.Texture.fromImage("resources/bg-mid.png");
      mid = new PIXI.Sprite(midTexture);
      mid.position.x = 0;
      mid.position.y = 128;
      stage.addChild(mid);
    
      renderer.render(stage);
    
      requestAnimFrame(update);
    }

Зазначений вище код говорить, що ми бажаємо викликати функцію update() кожного разу, коли вміст нашої сцени може бути перевідмальованим. Безперервний виклик requestAnimFrame() зазвичай призводить до відмальовування сцени зі швидкістю 60 разів на секунду, тобто з частотою кадрів в 60FPS. Функція update() буде виступати в ролі нашого основного циклу, але наразі в нас ще її немає. Саме вона буде відповідальна за виклик візуалізатора, тому позбавимось від рядка renderer.render(stage):

    var midTexture = PIXI.Texture.fromImage("resources/bg-mid.png");
      mid = new PIXI.Sprite(midTexture);
      mid.position.x = 0;
      mid.position.y = 128;
      stage.addChild(mid);
    
      //renderer.render(stage);
    
      requestAnimFrame(update);
    }

Добре. Тепер додамо функцію update() відразу після init():

    function update() {
      far.position.x -= 0.128;
      mid.position.x -= 0.64;
    
      renderer.render(stage);
    
      requestAnimFrame(update);
    }

Перші два рядка відповідають за оновлення позицій по горизонталі наших спрайтів. Зверніть увагу, що ми рухаємо дальній шар зі швидкістю 0,128 пікселів в той час як середній -- зі швидкістю 0,64 пікселів. Для того, щоб рухати шари ліворуч, ми вказали від'ємні значення. Також зверніть увагу, що pixi без дорікань працює з дробовими пікселями.

Наприкінці нашого циклу ми викликаємо функцію requestAnimFrame(), щоб переконатися в тому, що update() знов буде викликана, коли наше полотно буде готово для відмальовки наступного кадру.

Після створення головного циклу

Збережіть роботу та оновіть сторінку. Ми бачимо, що найдальший шар рухається повільніше, ніж середній, що створює певну ілюзію глибини, але також ми бачимо, що у нас є деякі проблеми в поточній реалізації. Наші спрайти йдуть вліво назавжди. Ми повинні змусити їх повертатися з правого боку. Є рішення.

Використання TilingSprite

Ми використовуємо PIXI.Sprite клас для відображення графіки, але pixi надає також декілька інших засобів для задоволення різних потреб. Якщо ви подивитесь на bg-far.png та bg-mid.png, то побачите що обидва зображення створені для того, щоб повторюватися горизонтально -- вони горизонтально безшовні.

Таким чином, замість "фізичного" переміщення самих спрайтів, не було б чудово, якби був спосіб рухати саму текстуру? На щастя pixi.js надає клас PIXI.TilingSprite. Почнемо за найдальшого шару. Видаліть закоментований рядок:

     var farTexture = PIXI.Texture.fromImage("resources/bg-far.png");
      //far = new PIXI.Sprite(farTexture);
      far.position.x = 0;
      far.position.y = 0;
      stage.addChild(far);
    }

Та замінить його іншим, щоб стало так:

    var farTexture = PIXI.Texture.fromImage("resources/bg-far.png");
      far = new PIXI.TilingSprite(farTexture, 512, 256);
      far.position.x = 0;
      far.position.y = 0;
      stage.addChild(far);
    }

Також додамо два нових параметра: far.tilePosition.x = 0 та far.tilePosition.x = 0:

    var farTexture = PIXI.Texture.fromImage("resources/bg-far.png");
      far = new PIXI.TilingSprite(farTexture, 512, 256);
      far.position.x = 0;
      far.position.y = 0;
      far.tilePosition.x = 0;
      far.tilePosition.y = 0;
      stage.addChild(far);
    }

Перед продовженням, давайте поговоримо про клас TilingSprite та його параметр tilePosition.

TilingSprite на відміну від Sprite потребує три параметра:

    far = new PIXI.TilingSprite(farTexture, 512, 256);

Перший параметр, як і колись є посиланням на текстуру, яку ви хочете використовувати. Другий і третій параметри -- це ширина і висота спрайта відповідно. Зазвичай ви будете встановлювати ці два параметри згідно з розмірами вашої текстури, в разі BG-far.png це 512х256.

У той час як ми жорстко задали ширину і висоту, ми могли витягнути значення цих параметрів з самої текстури. Ось альтернативна версія нашого коду:

    far = new PIXI.TilingSprite(
      farTexture,
      farTexture.baseTexture.width,
      farTexture.baseTexture.height
    );

Змінюючи параметр tilePosition ми імітуємо прокрутку без зміни фізичного положення спрайта. Тепер змінимо функцію update() згідно з нашими новими реаліями, замінивши far.position.x -= 0.128 на far.tilePosition.x -= 0.128:

    function update() {
      far.tilePosition.x -= 0.128;
      mid.position.x -= 0.64;
    
      renderer.render(stage);
    
      requestAnimFrame(update);
    }

Перезавантажте сторінку та подивіться на результат. Виконаємо тіж самі дії для середнього шару. Ось який вигляд має мати функція init() після того, як ми виконаємо всі необхідні маніпуляції:

    function init() {
      stage = new PIXI.Stage(0x66FF99);
      renderer = new PIXI.autoDetectRenderer(
        512,
        384,
        document.getElementById("game-canvas")
      );
    
      var farTexture = PIXI.Texture.fromImage("resources/bg-far.png");	
      far = new PIXI.TilingSprite(farTexture, 512, 256);
      far.position.x = 0;
      far.position.y = 0;
      far.tilePosition.x = 0;
      far.tilePosition.y = 0;
      stage.addChild(far);
    
      var midTexture = PIXI.Texture.fromImage("resources/bg-mid.png");
      mid = new PIXI.TilingSprite(midTexture, 512, 256);
      mid.position.x = 0;
      mid.position.y = 128;
      mid.tilePosition.x = 0;
      mid.tilePosition.y = 0;
      stage.addChild(mid);
    
      requestAnimFrame(update);
    }

Змінимо update() наступним чином:

    function update() {
      far.tilePosition.x -= 0.128;
      mid.tilePosition.x -= 0.64;
    
      renderer.render(stage);
    
      requestAnimFrame(update);
    }

Не забудьте подивитися на результат. Відтепер наші шари рухаються ідеально.

Висновок

Ми розглянули деякі з основ pixi.js і дізнались, як реалізувати нескінченно прокручування шарів з допомогою методу PIXI.TilingSprite.

Раджу вам самостійно поекспериментувати з pixi і почитати документацію та код зразків, котрі до неї додаються. Все це ви можете знайти в папці Pixi, що ви завантажили з GitHub.

Наступного разу

Хоча наш скролер і працює, він все ще залишається достатньо простим. Наступного разу ми познайомимося з такими важливими поняттями, як вьюпорт та позіціонування ігрового світу. Також ми додамо ще один шар до нашої гри, та зробимо наш витвір грою.

Також ми присвятимо багато часу на рефакторінг нашого поточного коду: реалізуємо більш об'єктно-орієнтовану архітектуру та позбавимось від глобальних змінних.

Я сподіваюсь, що для декого це керівництво виявилось корисним. Зустрінемось у наступній частині.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 6.2K
Приєднався: 7 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація