【PixiJS 覚書】第三回 PIXI.Sprite、PIXI.Text（とPIXI.Loaderのほんのさわりだけ）

##これまでのあらすじ
[第一回 PIXI.ApplicationとPIXI.Container](https://qiita.com/geregeregere/items/d8b7b51dd60f22d5e88a)
[第二回 PIXI.Graphics（と、PIXI.DisplayObjectの掘り下げ）](https://qiita.com/geregeregere/items/bc60ffc22618bb2a711e)

第一回でPixiJSの描画の基本構造をざっくり掴み、
第二回でPIXI.Graphicsの使い方を通して描画の仕組みや機能に慣れてもらいました。

この第三回ではいよいよスプライトを扱います。ようやくゲームっぽい表現ができるよ！
そしてPIXI.Spriteの派生クラスであるPIXI.Textもついでに紹介します。
また、「画像を読み込む」必要が発生しますので、データ読み込みの機能であるPIXI.Loaderにも軽く触れます。

##サンプルコード
```JavaScript:sample_code
//（前略）

app.loader.baseUrl = 'https://pixijs.io/examples/examples/assets/';

const resourceList = [{name: 'bunny', fileName: 'bunny.png'},
                    {name: 'flowerTop', fileName: 'flowerTop.png'},
                    {name: 'eggHead', fileName: 'eggHead.png'}];

for(let i = 0; i < resourceList.length; i++) {
    const {name, fileName} = resourceList[i];
    app.loader.add(name, fileName);
}

app.loader.load(startup);


function startup()
{
    for(let i = 0; i < resourceList.length; i++) {
        const { name } = resourceList[i];

        const sprite = new PIXI.Sprite(app.loader.resources[name].texture);
        sprite.anchor.set(0.5);
        sprite.x = 140 + 160 * i;
        sprite.y = 180;

        const text = new PIXI.Text(name, {fontFamily : 'Arial', fontSize: 24, fill : 0x101010, align : 'center'});
        text.anchor.set(0.5);
        text.x = 140 + 160 * i;
        text.y = 300;

        app.stage.addChild(sprite);
        app.stage.addChild(text);
    }
}
```

##実行結果
Playgroundで実行するとこうなります。
読み込んでいる画像は[PixiJS Examples](https://pixijs.io/examples/#/demos-basic/container.js)のサンプル画像です。いいセンスしてる。
![sprite_others.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107226/779a971b-2df5-96ef-917d-b96e073fcad1.png)

##PIXI.Loader
第一回の振り返り。
PIXI.Applicationには以下の７つの機能が用意されており、第一回では上の３つ`view`、`stage`、`screen`プロパティの持っている機能について確認しました。

|名前|機能|クラス|
|:-:|:-:|:-:|
|view|実際に画面に表示されるcanvas要素|HTMLCanvasElement|
|stage|viewに表示される描画オブジェクトのコンテナ|PIXI.Container|
|screen|viewの大きさなどの情報を持ったRectangle|PIXI.Rectangle|
|ticker|描画物のupdateループを担う|PIXI.Ticker|
|loader|ファイル読み込みと管理|PIXI.Loader|
|renderer|WebGLのレンダラ|PIXI.Renderer|
|resizeTo|viewの自動リサイズの<br>基準となるHTML要素|Windowまたは<br>HTMLElement|

今回は`loader`プロパティの機能について見ていきます。

####バージョンの違いに注意
第一回にも書きました、間が空いたのでもう一度。
「本記事は**v5**に基づいて記述しています」
なぜ改めてこれを強調するかというと、**PIXI.Loaderの使い方がv4とv5で変わっている**からです。

v4ではPIXIモジュールに静的に実装された機能であり、以下のように使用していました。

```JavaScript:v4
PIXI.loader.add('ファイルパス');
```
v5ではインスタンス化されたアプリケーションを介して利用します。

```JavaScript:v5
app.loader.add('ファイルパス');
```

繰り返しになりますが、**本記事ではv5**でいきます。

####おおまかな流れ
PIXI.Loaderによるファイル読み込みの大まかな流れは以下となります。

1. `add`メソッドで読み込み**キュー**へ追加
2. `load`メソッドで読み込み開始
3. 読み込み完了後に`resources`プロパティを参照

####addメソッド
```JavaScript:
for(let i = 0; i < resourceList.length; i++) {
    const {name, fileName} = resourceList[i];
    app.loader.add(name, fileName);
}
```
ではサンプルコードを追っていきましょう。
一行目の`baseUrl`プロパティやパス情報の配列はひとまず後回しにして、先に`add`メソッドから解説します。

このメソッドが便利であり同時に厄介な点は、読み込み元のパスの渡し方が柔軟で多岐にわたる点です。
実際にどのような渡し方があるかは[リファレンス]()にサンプルの記載があるのでそちらを参照していただくとして、ここでは最も初歩的な渡し方を紹介します。

・add('path');
または
・add('key', 'path');

PIXI.Loaderによって読み込まれたリソースはそれぞれ名前が付けられ、連想配列として管理されます。

ファイルパスを単体で渡すだけの場合、それによって読み込まれたリソースの名前はファイルパスがそのまま名前になります。

本記事のサンプルコードではパスだけでなくkeyも一緒に渡す二つ目の方法を取っており、その場合は'key'が連想配列のkeyとなります。
![add.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107226/f672caf5-114e-dca2-6520-a0ce0e34c32e.png)
なお、ここでは使いませんでしたが、パスの後にはオプションパラメータとして該当リソース読み込み完了後に呼び出されるコールバック関数を渡せます。
リソースの読み込みが一つ完了する毎にカウンタを増やす関数を渡してプログレスメーターを実装したりできそうですね。

####baseUrlプロパティ
```JavaScript:
app.loader.baseUrl = 'https://pixijs.io/examples/examples/assets/';

const resourceList = [{name: 'bunny', fileName: 'bunny.png'},
                    {name: 'flowerTop', fileName: 'flowerTop.png'},
                    {name: 'eggHead', fileName: 'eggHead.png'}];
```
順番前後しましたが、`baseUrl`プロパティを利用することで、ベースとなるパスを予め設定しておけます。まんまですね。
その結果、リソース情報を管理している配列`resourceList`内ではフルパスを記述せずにファイル名だけで済んでいます。

####loadメソッド
さて、`add`メソッドで読み込みたいリソースのパスをキューに登録できました。
次はこの読み込みキューを開始します。

```JavaScript:
app.loader.load(startup);
```
・load(callback)
キューに詰め込まれたリソースの読み込みを開始します。
読み込みは**非同期**で行われます。
だいじなことなのでもう一度言います。読み込みは**非同期**です。
リソース読み込みが完了する前に次の処理に進んでしまうとまずエラーになるので、読み込み完了に応じて次の処理をトリガーする必要があり、引数にはそのコールバック関数を渡します。

ちなみに`add`メソッド、`load`メソッドのどちらも戻り値としてloader自身(this)を返すので、リソースが一つだけでaddとloadを続けて行う場合には以下のようにメソッドチェーンで記述できます。

```JavaScript:
app.loader.add('bunny', 'bunny.png').load(startup);
```

####resourcesプロパティ
```JavaScript:
function startup()
{
    for(let i = 0; i < resourceList.length; i++) {
        const { name } = resourceList[i];

        const sprite = new PIXI.Sprite(app.loader.resources[name].texture);
    //（後略）
    }
}
```
`load`メソッドによるリソースの読み込みが完了し、`startup`が呼び出されました。
上述のように、読み込まれた各リソースは名前が付けられて連想配列として`resources`プロパティに格納されます。
そのため、リソースにアクセスするための名前をまず用意し、`resources[name]`として該当リソースを参照しています。

さて、ここまで何度も「リソースを読み込む」と表現してきましたが、読み込んだデータは具体的にどのような状態で保存されているのでしょうか。
その答えは[PIXI.LoaderResource](https://pixijs.download/dev/docs/PIXI.LoaderResource.html)クラスのインスタンスです。
PIXI.Loaderは読み込んだリソースの種類に応じて、このPIXI.LoaderResourceインスタンスの中身をイイ感じに作りこんでくれるのです。

・・・すみません、今「イイ感じに」というふわっとした表現に逃げました。

というのも、どの種類のファイルの場合にどういうデータとプロパティを持ったインスタンスが仕上がってくるのかは、各ファイルの種類や通信形式、DOM等の深い理解が必要なようで、これを詳細に紐解くのは現時点の私にはどうにも手に余りそうです。　∩(´・ω・`)∩ 降参
画像だけでなく音やjson、動画、text、xmlファイルなどを読み込んで扱えるようです。
（とはいえ、PixiJSは描画ライブラリなので音の再生機能はないですが。）

ともかく、いまスプライトを利用する上で必要になる部分だけは以下にまとめておこうと思います。

**【スプライトに利用する上でのresourceプロパティの要点】**
1. PIXI.Loaderは読み込むリソースの種類を拡張子から判別しており、対応している形式のファイルであれば自動的に下処理をしてくれる。
2. 取り扱えるデータの種類は [**→こちら**](https://pixijs.download/dev/docs/PIXI.LoaderResource.html#TYPE)
3. 今回のpngファイルのようなイメージ画像の場合、**読み込んだデータから自動的にPIXI.Textureインスタンスを作り出し、`texture`プロパティに格納してくれる**。
4. また、読み込んだデータそのものはimg要素として`data`プロパティに格納される。

**スプライトを利用する上では「3.」のテクスチャが重要**となります。
とりあえず必要なデータは読み込みました！
これ以上の深入りは避けてスプライトの話に移ります！

##PIXI.Sprite
PIXI.Loaderの話が長くなりましたが、ここからようやくスプライトに触れていきます。
####PIXI.SpriteとPIXI.Texture
```JavaScript:
        const sprite = new PIXI.Sprite(app.loader.resources[name].texture);
        sprite.anchor.set(0.5);
        sprite.x = 140 + 160 * i;
        sprite.y = 180;
```
リファレンス：[PIXI.Sprite](https://pixijs.download/dev/docs/PIXI.Sprite.html)

コンストラクタにテクスチャを渡してインスタンスを生成しています。
「テクスチャ」をより正確に言うとPIXI.Textureクラスのインスタンスです。

リファレンス：[PIXI.Texture](https://pixijs.download/dev/docs/PIXI.Texture.html)

さて、このPIXI.TextureはPIXI.DisplayObjectの派生クラス**ではありません**。
そのため、コンテナに直接`addChild`することはできず、主にスプライトに持たせる情報として使用します。

これでテクスチャという画像情報を持ったスプライトが生成されました。

####anchorプロパティ
PIXI.Spriteには`anchor`という使い勝手が良く利用頻度の高いプロパティが用意されています。
第二回で扱ったPIXI.DisplayObjectの`pivot`プロパティと良く似た働きをし、回転における基準軸やコンテナへ`addChild`する際の基準位置となる点は同じです。
ただし`pivot`がピクセル座標で指定するのに対し、`anchor`は画像の大きさに合わせた割合で指定します。
範囲は画像の左上を0、右下を1とし、割合で表します。つまり中央は「0.5」です。
大きさがまちまちで数が多いことが予想されるスプライトに対して、一々その画像のwidth、heightから中央位置を計算しなくて良いわけです。

```JavaScript:
//たとえばBunny.pngのスプライトの場合、width:26、height:37なので
//pivotだと画像の大きさに応じて中央位置を算出する必要がある
sprite.pivot.set(13, 18);

//anchorであれば画像の大きさに関係なく以下で中央を指定できる
sprite.anchor.set(0.5);

//（補足）setメソッドは引数を一つだけ渡すとx,y両方にセットしてくれます
```

これで画像の中央位置を指定できました。
あとはx座標、y座標を指定してスプライトの準備は完了です。

####おまけ
スプライトはコンストラクタにテクスチャを渡してインスタンス生成、と説明してきましたが、それ以外に画像を直に読みに行くお手軽メソッドも用意されていたりします。

```JavaScript:
PIXI.Sprite.from('https://pixijs.io/examples/examples/assets/bunny.png');
```
[リファレンス](https://pixijs.download/dev/docs/PIXI.Sprite.html#from)
指定先のファイルからテクスチャを作成してスプライトまで仕上げてくれます。

##PIXI.Text
最後は文字を表示させるPIXI.Textを扱います。
とは言っても、**PIXI.TextはPIXI.Spriteの派生クラス**であり、扱い方もほぼ同様です。

####インスタンス生成
```JavaScript:
        const text = new PIXI.Text(name, {fontFamily : 'Arial', fontSize: 24, fill : 0x101010, align : 'center'});
        text.anchor.set(0.5);
        text.x = 140 + 160 * i;
        text.y = 300;
```

リファレンス：[PIXI.Text](https://pixijs.download/dev/docs/PIXI.Text.html)

コンストラクタの引数は (text, style, canvas)の３つで、
・text：表示させたい文字列
・style：フォントのスタイルを指定するオブジェクト
・canvas：描画先となるcanvas要素
です。
最後のcanvasは今は忘れていいです。基本レベルにおいては描画先キャンバスはまず`view`なので。
というわけで、サンプルコードでは
fontFamily（フォントの種類）を 'Arial'に、
fontSize（フォントの大きさ）を 24px に、
fill（塗り色）を 0x101010（ほぼ黒）に
align（文字揃え）を 'center'に
してみました。
ちなみにalignは今回のように一行だけの場合は特に意味がありません。
テキストに改行が含まれて複数行になっていた場合に各行を'left'（左揃え）、'center'（中央揃え）、'right'（右揃え）にしてくれます。

テキストスタイルに他にどのような設定が可能であるかは [リファレンス](https://pixijs.download/dev/docs/PIXI.TextStyle.html) を参照してみてください。

あとはスプライトと同様に`anchor`とxy座標をセットして準備完了です。

####addChild
```JavaScript:
        app.stage.addChild(sprite);
        app.stage.addChild(text);
```
**忘れずに。**

##まとめ
**[PIXI.Loader]
・PIXI.Loaderはv4とv5で使い方が違う。
・v5ではPIXI.Applicationインスタンスの`loader`プロパティからアクセスする。
・基本手順：「add」して「load」して「resources」から参照
・pngその他のイメージファイルの場合、読み込んだ画像は自動的にリソースオブジェクトの`texture`プロパティにPIXI.Textureインスタンスとしてセットされる。**

**[PIXI.SpriteとPIXI.Texture]
・PIXI.Spriteインスタンスは画像情報としてPIXI.Textureインスタンスを持つ。
　コンストラクタで生成する場合、テクスチャを引数として渡す。
・スプライトの基準位置指定は`anchor`プロパティが便利。画像の大きさによらず「0～1」の範囲で指定できる。**

**[PIXI.Text]
・PIXI.TextはPIXI.Spriteの派生クラス。なので使い方も良く似ている。
・コンストラクターの引数にはテキストとフォントスタイルを渡す。**

------
今回はここまで！

次回、スプライトを動かすよ！やっと動く！