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;
|
|||
|
}
|
|||
|
```
|