🏴‍☠️
Сашка ☕
Blog  Tags 
💀 🔵 🔴

🧰 Pixi.js: Пример эффекта параллакс и использование ассетов

Опубликовано: 29 октября 2022 г.

Pixi.js 7.0.0

4 часа назад вышла 7–я версия библиотеки Pixi.js.

Для тех, кто уже использует Pixi.js подготовлен документ по миграции на новую версию.

В данном примере будем использовать загрузчик ассетов PIXI.Assets, который потеснил PIXI.Loader.
Подробнее написано здесь: v7-Migration-Guide#-replaces-loader-with-assets.

PIXI.Assets

PIXI.Assets — это универсальный инструмент для управления ресурсами в Pixi!
Суперсовременный и простой в использовании, с достаточной гибкостью, чтобы настраивать и делать то, что вам нужно!

Загрузка ассетов

Не бойся загружать данные несколько раз — Pixi.Assets НИКОГДА ничего не загрузит больше одного раза.

Например:

promise1 = PIXI.Assets.load('bunny.png')
promise2 = PIXI.Assets.load('bunny.png')

// promise1 === promise2

Вот несколько примеров загрузки ассетов:

// простой пример
PIXI.Assets.add('bunnyBooBoo', 'bunny.png');
const bunny = await PIXI.Assets.load('bunnyBooBoo');

// несколько значений
PIXI.Assets.add(['burger', 'chicken'], 'bunny.png');

const bunny = await PIXI.Assets.load('burger');
const bunny2 = await PIXI.Assets.load('chicken');

// передача параметров объекту
PIXI.Assets.add(
    'bunnyBooBooSmooth',
    'bunny{png,webp}',
    {scaleMode:SCALE_MODES.NEAREST},
);

Каркас приложения

HTML документ index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>pixi.js - assets &amp; parallax</title>
    <script type="text/javascript" src="pixi.js"></script>
</head>
<body>
    <script type="text/javascript" src="app.js"></script>
</body>
</html>

Код приложения app.js

let app;
let backgroundTextures;
let background = {
    sky: null,
    back: null,
    middle: null,
    front: null,
    x: 0,
    speed: 1,
};

/** Инициализация PIXI и настройка PIXI.Assets */
window.onload = function() {
    app = new PIXI.Application({
        width: 640,
        height: 360,
        backgroundColor: 0x2a2a3a,
    });
    document.body.appendChild(app.view);
}

/** Функция инициализации уровня */
function initLevel() {
    app.ticker.add(gameLoop);
}

/** Игровой цикл */
function gameLoop(delta) {
    // 
}

В переменной app я буду хранить объект PIXI.Application, чтобы можно было получать доступ к нему из других функций.

В переменную backgroundTextures будет загружать текстуры.

Переменная background отвечает за необходимые параметры, такие как скорость эффекта параллакса и положение тайлов изображений sky, back, middle, front.

В этом примере я буду использовать ассет Desert Parallax Background за авторством styloo.

В этом ассете есть фон, и три изображения с частями пустыни.

Добавление ассетов

Я распаковал ассет Desert Parallax Background в директорию assets, а также переименовал изображения.

Теперь в функцию window.onload = function() {} указываем ресурсы, которые я буду загружать и вызываем их загрузку.

PIXI.Assets.add('bg_back', '/assets/3.png');
PIXI.Assets.add('desert_2', '/assets/2.png');
PIXI.Assets.add('desert_1', '/assets/1.png');
PIXI.Assets.add('desert_0', '/assets/0.png');

let promise = PIXI.Assets.load(
    ['bg_back', 'desert_2', 'desert_1', 'desert_0'],
    (progress) => {
        console.log(`Assets loading progress: ${progress * 100}%`);
});

Функция PIXI.Assets.load принимает в качестве аргумента имя ассета, который необходимо загрузить, либо список ассетов.

Второй необязательный аргумент, это callback функция, которая вызывается во время загрузки Ассетов.
Функции передается параметр progress, который представляет процент (0.0 - 1.0) загруженных ассетов.

Функция возвращает Promise ассетов, которые были загружены.

По окончании загрузки ассетов, я вызываю функцию initLevel(), а также передаю в переменную backgroundTextures список загруженых ассетов, в данном случае это текстуры.

promise.then((value) => {
    backgroundTextures = value;
    initLevel();
});

Инициализация тайлов

Для создания тайлов, я написал функцию createBg(), которая создаёт объект PIXI.TilingSprite и устанавливает позицию на экране.

function createBg(texture) {
  let tiling = new PIXI.TilingSprite(texture, 640, 360);
  tiling.position.set(0, 0);
  app.stage.addChild(tiling);

  return tiling;
}

Создаёт тайлы:

function initLevel() {
    background.sky = createBg(backgroundTextures.bg_back);
    background.back = createBg(backgroundTextures.desert_2);
    background.middle = createBg(backgroundTextures.desert_1);
    background.front = createBg(backgroundTextures.desert_0);

    app.ticker.add(gameLoop);
}

Создаём эффект параллакса

Этот этап самый простой.

В игровом цикле я изменяю значение переменной background.x и смещаю позицию тайлов.

function gameLoop(delta) {
  background.x = (background.x - background.speed);
  background.front.tilePosition.x = background.x;
  background.middle.tilePosition.x = background.x / 2;
  background.back.tilePosition.x = background.x / 4;
}

Исходный код

const DEBUG = true;

let app;
let backgroundTextures;
let background = {
    sky: null,
    back: null,
    middle: null,
    front: null,
    x: 0,
    speed: 1,
};

/** PIXI and PIXI.Assets init */
window.onload = function() {
    app = new PIXI.Application({
        width: 640,
        height: 360,
        backgroundColor: 0x2a2a3a,
    });
    document.body.appendChild(app.view);

    PIXI.Assets.add('bg_back', '/assets/3.png');
    PIXI.Assets.add('desert_2', '/assets/2.png');
    PIXI.Assets.add('desert_1', '/assets/1.png');
    PIXI.Assets.add('desert_0', '/assets/0.png');

let promise = PIXI.Assets.load(
    ['bg_back', 'desert_2', 'desert_1', 'desert_0'],
    (progress) => {
        if (DEBUG)
            console.log(`Assets loading progress: ${progress * 100}%`);
    });

    promise.then((value) => {
        backgroundTextures = value;
        initLevel();
    });
}

/** Level init */
function initLevel() {
    background.sky = createBg(backgroundTextures.bg_back);
    background.back = createBg(backgroundTextures.desert_2);
    background.middle = createBg(backgroundTextures.desert_1);
    background.front = createBg(backgroundTextures.desert_0);

    app.ticker.add(gameLoop);
}

/** Game loop */
function gameLoop(delta) {
    background.x = (background.x - background.speed);
    background.front.tilePosition.x = background.x;
    background.middle.tilePosition.x = background.x / 2;
    background.back.tilePosition.x = background.x / 4;
}

/** Create background sprite */
function createBg(texture) {
    let tiling = new PIXI.TilingSprite(texture, 640, 360);
    tiling.position.set(0, 0);
    app.stage.addChild(tiling);

    return tiling;
}