PlayCanvasでスプライトアニメーション作成、そしてアニメーションからイベント発火

# 最初に
昔作ったゲームをブラウザゲームとして作り直そうと思っていて、

![kuchiinu_a_attack03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/8a618daf-b147-2ca2-2b60-4c2ce3a260d4.png)

こんなアニメーションをする画像をPlayCanvas上で使えるようにしたいなと考えています。

ちなみに以前作った環境ではメインループの中に入力も当たり判定も描画もぶっ込んでいるような、パワータイプなプログラムを書いていました。

今回はGUI環境に合わせてプログラムしていきたいと思います。


# アセットへスプライトの原料をアップロード
画像は透過PNGで行きます。
![スクリーンショット 2020-03-26 1.20.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/7373344e-7a1a-9c3f-13d2-31bba9ecc05b.png)

透過PNGってゲーム作るとき便利よね・・・
２５６色ビットマップしかゲームプログラムで使える選択肢がなくて、２５６色の中から透過色を一つ選ばねばならなかった頃が懐かしい・・・

# テクスチャーアトラスを作成
アップした画像で右クリックして「Create Texture Atlas」を選択
![スクリーンショット 2020-03-26 1.20.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/24cae063-dd1e-c090-f0db-ac35e3cf4682.png)

もとの画像はtypeが「texture」ですが、
![スクリーンショット 2020-03-26 1.21.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/4e2e4d88-6708-0970-2dd0-3d46c2d6b9b9.png)

新しく生成した方はtypeが「textureatlas」となっていて、アイコンが左上にあります。
![スクリーンショット 2020-03-26 1.21.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/f719724a-cbc1-f648-323b-4fa94e575279.png)

もとの画像の方はもう不要なので削除しても構いません。

# テクスチャアトラスをフレーム分けする
作成したテクスチャアトラスをダブルクリックするとスプライトエディタが開きます。
![スクリーンショット 2020-03-26 1.23.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/ac431ef1-de35-3682-4ddf-b0da7c6d8a8a.png)

この画像をスプライトアニメーションのコマとなるフレームに分けていきます。

![スクリーンショット 2020-03-26 1.23.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/22d12c87-1961-b7ea-a971-fde5290a4b31.png)

本来はマウスドラッグで1個1個作っていくのですが、とっっっても面倒くさいのでここで手品を使います。
（同じ大きさの矩形がお行儀よく並んでいること前提なのですが）

以下のJSONファイルを「UPLOAD TEXTURE PACKER JSON」というボタンからアップロードしてください。

```spritesheet.json
{
	"frames": {
		"rei_00": {
			"frame": {"x": 0,"y": 0,"w": 256,"h": 256}
		},
		"rei_01": {
			"frame": {"x": 256,"y": 0,"w": 256,"h": 256}
		},
		"rei_02": {
			"frame": {"x": 512,"y": 0,"w": 256,"h": 256}
		},
		"down_00": {
			"frame": {"x": 0,"y": 256,"w": 256,"h": 256}
		},
		"down_01": {
			"frame": {"x": 256,"y": 256,"w": 256,"h": 256}
		},
		"down_02": {
			"frame": {"x": 512,"y": 256,"w": 256,"h": 256}
		},
		"down_03": {
			"frame": {"x": 768,"y": 256,"w": 256,"h": 256}
		},
		"a_stand_00": {
			"frame": {"x": 0,"y": 512,"w": 256,"h": 256}
		},
		"a_front_00": {
			"frame": {"x": 256,"y": 512,"w": 256,"h": 256}
		},
		"a_back_00": {
			"frame": {"x": 512,"y": 512,"w": 256,"h": 256}
		},
		"a_parry_00": {
			"frame": {"x": 0,"y": 768,"w": 256,"h": 256}
		},
		"a_parry_01": {
			"frame": {"x": 256,"y": 768,"w": 256,"h": 256}
		},
		"a_receive_00": {
			"frame": {"x": 512,"y": 768,"w": 256,"h": 256}
		},
		"a_receive_01": {
			"frame": {"x": 768,"y": 768,"w": 256,"h": 256}
		},
		"a_attack_x00": {
			"frame": {"x": 0,"y": 1024,"w": 256,"h": 256}
		},
		"a_attack_x01": {
			"frame": {"x": 256,"y": 1024,"w": 256,"h": 256}
		},
		"a_attack_x02": {
			"frame": {"x": 512,"y": 1024,"w": 256,"h": 256}
		},
		"a_attack_y00": {
			"frame": {"x": 0,"y": 1280,"w": 256,"h": 256}
		},
		"a_attack_y01": {
			"frame": {"x": 256,"y": 1280,"w": 256,"h": 256}
		},
		"a_attack_y02": {
			"frame": {"x": 512,"y": 1280,"w": 256,"h": 256}
		},
		"a_attack_z00": {
			"frame": {"x": 0,"y": 1536,"w": 256,"h": 256}
		},
		"a_attack_z01": {
			"frame": {"x": 256,"y": 1536,"w": 256,"h": 256}
		},
		"a_attack_z02": {
			"frame": {"x": 512,"y": 1536,"w": 256,"h": 256}
		},
		"b_stand_00": {
			"frame": {"x": 0,"y": 1792,"w": 256,"h": 256}
		},
		"b_front_00": {
			"frame": {"x": 256,"y": 1792,"w": 256,"h": 256}
		},
		"b_back_00": {
			"frame": {"x": 512,"y": 1792,"w": 256,"h": 256}
		},
		"b_parry_00": {
			"frame": {"x": 0,"y": 2048,"w": 256,"h": 256}
		},
		"b_parry_01": {
			"frame": {"x": 256,"y": 2048,"w": 256,"h": 256}
		},
		"b_receive_00": {
			"frame": {"x": 512,"y": 2048,"w": 256,"h": 256}
		},
		"b_receive_01": {
			"frame": {"x": 768,"y": 2048,"w": 256,"h": 256}
		},
		"b_attack_x00": {
			"frame": {"x": 0,"y": 2304,"w": 256,"h": 256}
		},
		"b_attack_x01": {
			"frame": {"x": 256,"y": 2304,"w": 256,"h": 256}
		},
		"b_attack_x02": {
			"frame": {"x": 512,"y": 2304,"w": 256,"h": 256}
		},
		"b_attack_y00": {
			"frame": {"x": 0,"y": 2560,"w": 256,"h": 256}
		},
		"b_attack_y01": {
			"frame": {"x": 256,"y": 2560,"w": 256,"h": 256}
		},
		"b_attack_y02": {
			"frame": {"x": 512,"y": 2560,"w": 256,"h": 256}
		},
		"b_attack_z00": {
			"frame": {"x": 0,"y": 2816,"w": 256,"h": 256}
		},
		"b_attack_z01": {
			"frame": {"x": 256,"y": 2816,"w": 256,"h": 256}
		},
		"b_attack_z02": {
			"frame": {"x": 512,"y": 2816,"w": 256,"h": 256}
		}
	},
	"meta": {
		"app": "",
		"version": "1.0",
		"image": "nuemaru.png",
		"format": "RGBA8888",
		"size": {"w": 1024,"h": 4096},
		"scale": "1"
	}
}
```

![スクリーンショット 2020-03-26 2.03.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/dd2d333a-cb8e-61fa-887f-0b5f3a868917.png)

JSONをアップロードすると自動で２５６×２５６のフレームに切り分けられます。
![スクリーンショット 2020-03-26 15.09.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/783b2d5f-eca7-a0af-5544-a0b44ae22aca.png)

使ったJSONファイルは1024×4096サイズの画像を256×256のフレームを切り分けるように記述してあります。
使う際は書き換えてください。

# スプライトを作る
前項のフレーム分けでできたrei_00,rei_01,rei_02をつなげてスプライトをつくります。

一番最初にくるフレームを選択して「NEW SPRITE FROM SELECTION」をクリック。
![スクリーンショット 2020-03-26 15.16.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/2bd40712-eed6-79a7-d628-4b16c56acd0c.png)

「SPRITE ASSETS」に「rei_00」というスプライトが作成されます。
続けてフレームを追加する場合は「ADD FRAMES TO SPRITE ASSET」をクリック。
![スクリーンショット 2020-03-26 15.19.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/123239c1-00e0-0a5f-e71d-f664a8243993.png)

追加したいフレームを選択して「ADD SELECTED FRAMES」をクリック。
![スクリーンショット 2020-03-26 15.24.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/e5ec6658-0903-2313-78b3-17f66cd9c43d.png)

追加完了。
![スクリーンショット 2020-03-26 15.25.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/d3bf5194-e33a-63c5-8c07-0faddf3db02d.png)


# スプライトアニメーションの土台を作る
Root直下で「Animated Sprite」のエンティティを追加します。
![スクリーンショット 2020-03-26 1.19.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/6c9a96dc-aed5-520c-e765-af337f863a08.png)


# スプライトをエンティティにセットする。
![スクリーンショット 2020-03-26 15.34.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/3de0ae2f-052c-40d9-91bc-026f94b18628.png)

これでLaunchするとアニメーションしているのが確認できます。
<blockquote class="twitter-tweet"><a href="https://twitter.com/bcosizm/status/1243068551004487680"/></blockquote>

# スプライトからイベントを発火してみる。
ここまでくると攻撃などのアニメーション開始時に移動や攻撃判定などの処理を追加したくなるはずです。

ちょっとだけ試してみましょう。

AnimatedSpriteにスクリプトを追加し、以下のコードを追加します。

```js
CtrlPlayer.prototype.initialize = function() {

/* 中略 */

this.entity.sprite.clip("Clip 1").on("loop",this.looploop);

}

CtrlPlayer.prototype.looploop = function(){
    console.log("looploop");
    return;
};
```

これで"Clip 1"のスプライトがループするときにイベントが起きるようになるので、イベントハンドラ内に移動なり当たり判定を発生させるなり煮るなり焼くなり好きにできるようになります。

他のスプライトアニメーションにイベントハンドラをセットしたい場合は、clipメソッドの引数を別のスプライトアニメーションの名前に変えてみましょう。

[pc.SpriteAnimationClip](https://developer.playcanvas.com/en/api/pc.SpriteAnimationClip.html)の説明によるとイベントは繰り返し発生時のloop以外にも

- end
- pause
- play
- resume
- stop

があります。

わがままを言えば、各アニメーションのフレームが変わる時・・・例えば４フレーム目だけで起こすイベントなんかを設定できたらいいんだけどなあーと思っています。（こういうのは自分で作るべき？）