287 lines
9.0 KiB
Markdown
287 lines
9.0 KiB
Markdown
---
|
||
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 & 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;
|
||
}
|
||
```
|