Blog/content/posts/pixijs/assets-and-parallax.md

287 lines
9.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "🧰 Pixi.js: Пример эффекта параллакс и использование ассетов"
date: 2022-10-29T00:24:59+03:00
draft: false
tags: [pixijs, gamedev, tutorial, javascript]
---
## Pixi.js 7.0.0
4 часа назад вышла [7я версия](https://github.com/pixijs/pixijs/releases/tag/v7.0.0)
библиотеки [Pixi.js](https://pixijs.com).
Для тех, кто уже использует Pixi.js подготовлен
[документ](https://github.com/pixijs/pixijs/wiki/v7-Migration-Guide)
по миграции на новую версию.
В данном примере будем использовать загрузчик ассетов [PIXI.Assets](https://pixijs.download/release/docs/PIXI.Assets.html),
который потеснил [PIXI.Loader](https://pixijs.download/release/docs/PIXI.Loader.html).
Подробнее написано здесь: [v7-Migration-Guide#-replaces-loader-with-assets](https://github.com/pixijs/pixijs/wiki/v7-Migration-Guide#-replaces-loader-with-assets).
## PIXI.Assets
> PIXI.Assets — это универсальный инструмент для управления ресурсами в Pixi!
> Суперсовременный и простой в использовании, с достаточной гибкостью,
> чтобы настраивать и делать то, что вам нужно!
### Загрузка ассетов
Не бойся загружать данные несколько раз —
Pixi.Assets **НИКОГДА** ничего не загрузит больше одного раза.
Например:
```javascript
promise1 = PIXI.Assets.load('bunny.png')
promise2 = PIXI.Assets.load('bunny.png')
// promise1 === promise2
```
Вот несколько примеров загрузки ассетов:
```javascript
// простой пример
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`:
```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`
```javascript
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](https://styloo.itch.io/desert-parallax-background)
за авторством **styloo**.
В этом ассете есть фон, и три изображения с частями пустыни.
## Добавление ассетов
Я распаковал ассет **Desert Parallax Background** в директорию `assets`,
а также переименовал изображения.
Теперь в функцию `window.onload = function() {}` указываем ресурсы,
которые я буду загружать и вызываем их загрузку.
```javascript
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` список загруженых ассетов,
в данном случае это текстуры.
```javascript
promise.then((value) => {
backgroundTextures = value;
initLevel();
});
```
## Инициализация тайлов
Для создания тайлов, я написал функцию `createBg()`, которая создаёт объект
[PIXI.TilingSprite](https://pixijs.download/release/docs/PIXI.TilingSprite.html)
и устанавливает позицию на экране.
```javascript
function createBg(texture) {
let tiling = new PIXI.TilingSprite(texture, 640, 360);
tiling.position.set(0, 0);
app.stage.addChild(tiling);
return tiling;
}
```
Создаёт тайлы:
```javascript
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` и
смещаю позицию тайлов.
```javascript
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;
}
```
## Исходный код
```javascript
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;
}
```