アセット
The Assets パッケージ
The Assets パッケージは、旧式のLoader
クラスに代わる最新のソリューションです。これはPromiseベースのリソース管理ソリューションで、アセットのダウンロード、キャッシュ、そして使用可能な形式への解析を行います。ダウンロードは同時に行われ、バックグラウンドで行われるため、アプリの起動時間が短縮されます。キャッシュにより、同じアセットを二度とダウンロードする必要がなくなり、拡張可能なパーサーシステムにより、プロセスを簡単に拡張およびカスタマイズできます。
はじめに
Assets
は、最新のブラウザですべてサポートされているJavaScript Promisesに大きく依存しています。ただし、ターゲットブラウザがPromisesをサポートしていない場合は、ポリフィルを検討する必要があります。
最初のAssets Promiseの作成
Assets
インスタンスを簡単に使用するには、Assets.load
を呼び出してアセットを渡すだけです。これにより、解決されると目的の値が得られるPromiseが返されます。この例では、テクスチャを読み込み、それをスプライトに変換します。
import { Application, Assets, Sprite } from 'pixi.js';
// Create a new application
const app = new Application();
// Initialize the application
await app.init({ background: '#1099bb', resizeTo: window });
// Append the application canvas to the document body
document.body.appendChild(app.canvas);
// Start loading right away and create a promise
const texturePromise = Assets.load('https://pixijs.dokyumento.jp/assets/bunny.png');
// When the promise resolves, we have the texture!
texturePromise.then((resolvedTexture) =>
{
// create a new Sprite from the resolved loaded Texture
const bunny = Sprite.from(resolvedTexture);
// center the sprite's anchor point
bunny.anchor.set(0.5);
// move the sprite to the center of the screen
bunny.x = app.screen.width / 2;
bunny.y = app.screen.height / 2;
app.stage.addChild(bunny);
});
Assets
を使用する際に非常に重要なことは、すべてのリクエストがキャッシュされ、URLが同じであれば、返されるPromiseも同じであるということです。コードで示すと
promise1 = Assets.load('bunny.png')
promise2 = Assets.load('bunny.png')
// promise1 === promise2
すぐに使用できるアセットタイプは以下のとおりです。外部プラグインは必要ありません。
- テクスチャ (
avif
、webp
、png
、jpg
、gif
) - スプライトシート (
json
) - ビットマップフォント (
xml
、fnt
、txt
) - Webフォント (
ttf
、woff
、woff2
) - JSONファイル (
json
) - テキストファイル (
txt
)
ローダーパーサーを追加することで、さらに多くのタイプを簡単に追加できます。
認識できないURLの処理
基本的な構文では、アセットタイプはファイル拡張子によって認識されます。たとえば、https://pixijs.dokyumento.jp/assets/bunny.png
は.png
で終わるため、Assets.load
はテクスチャローダーを使用する必要があると判断できます。
場合によっては、URLを制御できない場合があり、認識可能な拡張子がないあいまいなURLを処理する必要があります。このような状況では、明示的なローダーを指定できます。
promise = Assets.load({
src: 'https://example.com/ambiguous-file-name',
loader: 'loadTextures'
})
使用できるloader
の値をいくつか示します。
- テクスチャ:
loadTextures
- Webフォント:
loadWebFont
- JSONファイル:
loadJson
- テキストファイル:
loadTxt
解決済みのPromiseに関する警告
アセットがダウンロードされると、Assets
インスタンス内にPromiseとしてキャッシュされ、再度ダウンロードしようとすると、既に解決済みのPromiseへの参照が得られます。しかし、Promiseハンドラー.then(...)
/.catch(...)
/.finally(...)
は常に非同期であるため、Promiseが既に解決されていても、.then(...)
/.catch(...)
/.finally(...)
の下のコードは、それらの内部のコードよりも先に実行されます。この例を参照してください。
console.log(1);
alreadyResolvedPromise.then(() => console.log(2));
console.log(3);
// Console output:
// 1
// 3
// 2
これが発生する理由の詳細については、Microtaskについて学ぶ必要がありますが、async関数の使用によってこの問題を軽減できます。
Async/Awaitの使用
より直感的で読みやすいPromiseの使用方法があります。async
/await
です。
これを使用するには、まず関数/メソッドを作成し、async
としてマークする必要があります。
async function test() {
// ...
}
この関数は、返された値をPromiseでラップし、Promiseの前にawait
キーワードを使用して、Promiseが解決されるまでコードの実行を停止し、値を取得できます。
この例を参照してください。
// Create a new application
const app = new Application();
// Initialize the application
await app.init({ background: '#1099bb', resizeTo: window });
// Append the application canvas to the document body
document.body.appendChild(app.canvas);
const texture = await Assets.load('https://pixijs.dokyumento.jp/assets/bunny.png');
// Create a new Sprite from the awaited loaded Texture
const bunny = Sprite.from(texture);
// Center the sprite's anchor point
bunny.anchor.set(0.5);
// Move the sprite to the center of the screen
bunny.x = app.screen.width / 2;
bunny.y = app.screen.height / 2;
app.stage.addChild(bunny);
texture
変数は、これでPromiseではなく、このPromiseが解決された後に得られた解決済みのテクスチャになります。
const texture = await Assets.load('examples/assets/bunny.png');
これにより、コールバック地獄に陥ることなく、より読みやすいコードを作成し、プログラムが停止して値を返すタイミングをより適切に把握できます。
複数アセットの読み込み
Assets.add(...)
を使用してアセットをキャッシュに追加し、読み込むすべてのキーを使用してAssets.load(...)
を呼び出すことで、すべてのアセットを同時に読み込むことができます。次の例を参照してください。
// Append the application canvas to the document body
document.body.appendChild(app.canvas);
// Add the assets to load
Assets.add({ alias: 'flowerTop', src: 'https://pixijs.dokyumento.jp/assets/flowerTop.png' });
Assets.add({ alias: 'eggHead', src: 'https://pixijs.dokyumento.jp/assets/eggHead.png' });
// Load the assets and get a resolved promise once both are loaded
const texturesPromise = Assets.load(['flowerTop', 'eggHead']); // => Promise<{flowerTop: Texture, eggHead: Texture}>
// When the promise resolves, we have the texture!
texturesPromise.then((textures) =>
{
// Create a new Sprite from the resolved loaded Textures
const flower = Sprite.from(textures.flowerTop);
flower.anchor.set(0.5);
flower.x = app.screen.width * 0.25;
flower.y = app.screen.height / 2;
app.stage.addChild(flower);
const egg = Sprite.from(textures.eggHead);
egg.anchor.set(0.5);
egg.x = app.screen.width * 0.75;
egg.y = app.screen.height / 2;
app.stage.addChild(egg);
});
ただし、@pixi/Assets
を最大限に活用するには、バンドルを使用する必要があります。バンドルはアセットをグループ化するための方法であり、Assets.addBundle(...)
/Assets.loadBundle(...)
を呼び出すことで手動で追加できます。
Assets.addBundle('animals', {
bunny: 'bunny.png',
chicken: 'chicken.png',
thumper: 'thumper.png',
});
const assets = await Assets.loadBundle('animals');
ただし、バンドルを処理する最適な方法は、マニフェストを使用し、そのマニフェスト(またはさらに良いことに、それを指すURL)を使用してAssets.init({manifest})
を呼び出すことです。アセットをアプリの画面またはステージに対応するバンドルに分割すると、ユーザーがアプリを使用している間にバックグラウンドで読み込みが行われ、単一の巨大な読み込み画面にユーザーを閉じ込める必要がなくなるため、便利です。
{
"bundles":[
{
"name":"load-screen",
"assets":[
{
"alias":"background",
"src":"sunset.png"
},
{
"alias":"bar",
"src":"load-bar.{png,webp}"
}
]
},
{
"name":"game-screen",
"assets":[
{
"alias":"character",
"src":"robot.png"
},
{
"alias":"enemy",
"src":"bad-guy.png"
}
]
}
]
}
Assets.init({manifest: "path/manifest.json"});
init
は一度だけ呼び出すことができます。
URLを繰り返しても欠点はありません。すべてがキャッシュされるため、2つのバンドルで同じアセットが必要な場合は、追加コストなしでリクエストを複製できます。
バックグラウンド読み込み
従来の読み込み方法は、Loader
を使用してアプリの開始時にすべてのアセットを読み込むことでしたが、ユーザーの忍耐力は低下しており、コンテンツはすぐに利用可能であることが求められています。そのため、ユーザーにコンテンツを表示するために必要な最小限のものをロードし、ユーザーが操作している間にバックグラウンドで次のコンテンツを読み込むという方法に移行しています。
幸いなことに、Assets
には、すべてをバックグラウンドで読み込み、すぐにアセットが必要な場合はキューの先頭に移動できるシステムが備わっています。これにより、読み込み時間を最小限に抑えることができます。
これを実現するために、Assets.backgroundLoad(...)
とAssets.backgroundLoadBundle(...)
メソッドがあります。これにより、これらのアセットがバックグラウンドでパッシブに読み込みを開始します。そのため、最終的にそれらの読み込みを行うと、すぐに読み込まれたアセットが解決されるPromiseが得られます。
最終的にアセットを表示する必要がある場合は、通常のAssets.load(...)
またはAssets.loadBundle(...)
を呼び出すと、対応するPromiseが得られます。
これを行う最適な方法は、バンドルを使用することです。次の例を参照してください。
import { Application, Assets, Sprite } from 'pixi.js';
// Create a new application
const app = new Application();
async function init()
{
// Initialize the application
await app.init({ background: '#1099bb', resizeTo: window });
// Append the application canvas to the document body
document.body.appendChild(app.canvas);
// Manifest example
const manifestExample = {
bundles: [
{
name: 'load-screen',
assets: [
{
alias: 'flowerTop',
src: 'https://pixijs.dokyumento.jp/assets/flowerTop.png',
},
],
},
{
name: 'game-screen',
assets: [
{
alias: 'eggHead',
src: 'https://pixijs.dokyumento.jp/assets/eggHead.png',
},
],
},
],
};
await Assets.init({ manifest: manifestExample });
// Bundles can be loaded in the background too!
Assets.backgroundLoadBundle(['load-screen', 'game-screen']);
}
init();
ゲームに含まれる各画面ごとに1つのバンドルを作成し、アプリの開始時にすべてダウンロードを開始するように設定します。ユーザーがアプリでゆっくりと進歩した場合、最初の読み込み画面の後、読み込み画面が表示されることはありません。