Siv3DでSpriteStudioのアニメーション（ssbpLib）を動かしてみる

# はじめに
ゲームを作りたい･･････。そんな想いからSiv3Dにたどり着いた人は私と同じようにたくさん居ると思います。しかし素材を全て一から作るのは非常に骨の折れる作業です。

そこで今回Siv3DでSpriteStudioを用いて作ったアニメーションを動かしてみようと思います。
![test.gif](https://qiita-image-store.s3.amazonaws.com/0/153987/34975da0-cc13-a8b7-d883-d7bffb497baf.gif)

# SpriteStudioとはなんぞ？
スプライトアニメーションを手軽に作れるというものです。
インディーズであれば無料で使え、商用利用も可能なので導入も非常に気楽に行えます。

実際どんなことが出来るかというのは公式のページを見たほうがはやいと思います。
http://www.webtech.co.jp/spritestudio/

公式ででかでかと書いてある通り、SpriteStudioは「超汎用データ」ということで、様々なプラットフォームに対応できるようになっています。

# ssbpLibとはなんぞ？
> OPTPiX SpriteStudio 5 で制作したアニメーションデータを再生するための汎用C++ライブラリです。
C++で作られたゲームやアプリに使用する事ができます。

詳しくはこちらを参照してください。
http://www.webtech.co.jp/blog/products/spritestudio/7731/

やっていることとしてはSpriteStudioで作成したプロジェクトファイルを専用の形式に変換したものを用いてファイルの解析とアニメーションの制御をしてくれるものです。

つまり、
- データの読み込み
- 描画

この二つをプログラマが書いてあげれば簡単にC++のプログラムに組み込むことが出来ます。


# 早速組み込もう！
http://www.webtech.co.jp/blog/products/spritestudio/7731/
こちらを参考にしながら実際にSiv3Dへと組み込んでみましょう。
サンプルはDxLibですが、Siv3D用に書き換えながら組み込んでいきます。

## １．ssbpLibのダウンロード
まず、こちらからssbpLibをダウンロードしましょう。
https://github.com/SpriteStudio/ssbpLib

## ２．プロジェクトの作成
今回はSiv3D(August2016v2)を用いています。
![01.png](https://qiita-image-store.s3.amazonaws.com/0/153987/c7fe62f1-f662-5b8e-ea39-6ea8c9bb1690.png)

こんな感じでファイルをプロジェクトに追加してください。

## ３．プログラムの書き換え
今のままではDxLib用なのでSiv3D用にプログラムを書き換えていきます。
最初に書き換えるのは`SS5Player.h`です。
Siv3Dは上がマイナスなので811行目の定義をコメントをはずして有効にしておきましょう。
他にも細かな定義があったりしますが、全部実装するのは大変なのでSiv3Dで動くように最小限の変更だけにしておきましょう。

```cpp:SS5Player.h
#define UP_MINUS
```

次に書き換えるのは`SS5PlayerPlatform.cpp`です

```cpp:SS5PlayerPlatform.cpp
// 
//  SS5Platform.cpp
//
#include "SS5PlayerPlatform.h"

#include <Siv3D.hpp>
```

まずは冒頭、このサンプルはDXライブラリを用いているのでそこをSiv3D.hppに変えましょう。


```cpp:SS5PlayerPlatform.cpp
	/**
	* テクスチャの読み込み
	*/
	long SSTextureLoad(const char* pszFileName, SsTexWrapMode::_enum  wrapmode, SsTexFilterMode::_enum filtermode)
	{
		/**
		* テクスチャ管理用のユニークな値を返してください。
		* テクスチャの管理はゲーム側で行う形になります。
		* テクスチャにアクセスするハンドルや、テクスチャを割り当てたバッファ番号等になります。
		*
		* プレイヤーはここで返した値とパーツのステータスを引数に描画を行います。
		*/
		static long rc = -1;
        rc++;

        size_t pReturnValue;
        wchar_t wcstr[64];	
        const char *mbstr;		
        mbstowcs_s(&pReturnValue, wcstr, 64, pszFileName, _TRUNCATE);

        TextureAsset::Register(Format(rc), wcstr);

		return rc;
	}
	
	/**
	* テクスチャの解放
	*/
	bool SSTextureRelese(long handle)
	{
        TextureAsset::Release(Format(handle));

		return true ;
	}

	/**
	* テクスチャのサイズを取得
	* テクスチャのUVを設定するのに使用します。
	*/
	bool SSGetTextureSize(long handle, int &w, int &h)
	{
        w = TextureAsset(Format(handle)).size.x;
        h = TextureAsset(Format(handle)).size.y;
		
		return true;
	}

```

次に`SSTextureLoad`　`SSTextureRelese`　`SSGetTextureSize`この3つの関数を書き換えます。DXライブラリではテクスチャの生成を行うとハンドルが帰ってきますが、Siv3Dでは基本的にプログラマがキーを作りそれを基にテクスチャの生成をするので、自動で一意のキーを生成するようにしています。
テクスチャのサイズの取得等もSiv3Dの仕様に合わせたものにしています。

残すは描画の処理だけなのですが、今回はSiv3DのSpriteを用いて描画をしようと思うのでそのための下準備をします。

```cpp:SS5Player.h
struct State
{
    Sprite sprite;
    ...   
    ...


    void init()
    {
        sprite = Sprite(4, 6);
        sprite.indices = { 0, 1, 2, 1, 2, 3 };
        ...
        ...
```

最後に描画処理をいじります。細かな対応はしていないので必要であれば随時対応していきます。





```cpp:SS5PlayerPlatform.cpp
	/**
	* スプライトの表示
	*/
    void SSDrawSprite(State state)
    {
        SSV3F_C4B_T2F_Quad quad;
        quad = state.quad;
        float cx = ((state.rect.size.width) * -(state.pivotX - 0.5f));
        float cy = ((state.rect.size.height) * -(state.pivotY - 0.5f));

        quad.tl.vertices.x += cx;
        quad.tl.vertices.y += cy;
        quad.tr.vertices.x += cx;
        quad.tr.vertices.y += cy;
        quad.bl.vertices.x += cx;
        quad.bl.vertices.y += cy;
        quad.br.vertices.x += cx;
        quad.br.vertices.y += cy;

        float t[16];
        TranslationMatrix(t, quad.tl.vertices.x, quad.tl.vertices.y, 0.0f);
        MultiplyMatrix(t, state.mat, t);
        quad.tl.vertices.x = t[12];
        quad.tl.vertices.y = t[13];
        TranslationMatrix(t, quad.tr.vertices.x, quad.tr.vertices.y, 0.0f);
        MultiplyMatrix(t, state.mat, t);
        quad.tr.vertices.x = t[12];
        quad.tr.vertices.y = t[13];
        TranslationMatrix(t, quad.bl.vertices.x, quad.bl.vertices.y, 0.0f);
        MultiplyMatrix(t, state.mat, t);
        quad.bl.vertices.x = t[12];
        quad.bl.vertices.y = t[13];
        TranslationMatrix(t, quad.br.vertices.x, quad.br.vertices.y, 0.0f);
        MultiplyMatrix(t, state.mat, t);
        quad.br.vertices.x = t[12];
        quad.br.vertices.y = t[13];

        quad.tl.colors.a = quad.tl.colors.a * state.Calc_opacity / 255;
        quad.tr.colors.a = quad.tr.colors.a * state.Calc_opacity / 255;
        quad.bl.colors.a = quad.bl.colors.a * state.Calc_opacity / 255;
        quad.br.colors.a = quad.br.colors.a * state.Calc_opacity / 255;

        state.sprite.vertices[0].set(quad.tl.vertices.x, quad.tl.vertices.y, quad.tl.texCoords.u, quad.tl.texCoords.v, Color(quad.tl.colors.r, quad.tl.colors.g, quad.tl.colors.b, quad.tl.colors.a));
        state.sprite.vertices[1].set(quad.tr.vertices.x, quad.tr.vertices.y, quad.tr.texCoords.u, quad.tr.texCoords.v, Color(quad.tr.colors.r, quad.tr.colors.g, quad.tr.colors.b, quad.tr.colors.a));
        state.sprite.vertices[2].set(quad.bl.vertices.x, quad.bl.vertices.y, quad.bl.texCoords.u, quad.bl.texCoords.v, Color(quad.bl.colors.r, quad.bl.colors.g, quad.bl.colors.b, quad.bl.colors.a));
        state.sprite.vertices[3].set(quad.br.vertices.x, quad.br.vertices.y, quad.br.texCoords.u, quad.br.texCoords.v, Color(quad.br.colors.r, quad.br.colors.g, quad.br.colors.b, quad.br.colors.a));

        state.sprite.draw(TextureAsset(Format(state.texture.handle)));

    }
```

サンプルの処理をそのままSiv3Dで書きました。
後は実際にメインで動かしてみるだけです。


```cpp:Main.cpp
# include <Siv3D.hpp>
# include "SS5Player.h"

void Main()
{
    s3d::Window::Resize(960, 540);
    Graphics::SetBackground(Palette::Slategray);

    ss::Player *ssplayer;
    ss::ResourceManager *resman;

    resman = ss::ResourceManager::getInstance();
    ssplayer = ss::Player::create();

    resman->addData("character_template_comipo\\character_template1.ssbp");
    ssplayer->setData("character_template1");         // ssbpファイル名（拡張子不要）
                                                      //再生するモーションを設定
    ssplayer->play("character_template_3head/stance");        // アニメーション名を指定(ssae名/アニメーション名)

                                                              //表示位置を設定
    ssplayer->setPosition(200, 350);
    //スケール設定
    ssplayer->setScale(0.5f, 0.5f);
    //回転を設定
    ssplayer->setRotation(0.0f, 0.0f, 0.0f);
    //透明度を設定
    ssplayer->setAlpha(255);
    //反転を設定
    ssplayer->setFlip(false, false);


    float i = 1.0f / 60.0f;
    ss::ResluteState result;
    const Font font(12);
    while (System::Update())
    {
        ssplayer->getPartState(result, "body");

        ssplayer->update(i);                 //プレイヤーの更新
        ssplayer->draw();                 //プレイヤーの描画

                                          //取得座用の表示
        font.draw(Format(L"body = x:", result.x, L" y:", result.y), 0, 0);
        font.draw(Format(L"frame:", ssplayer->getFrameNo(), L" drawCount:", ssplayer->getDrawSpriteCount()), 0, 16);
    }

    delete (ssplayer);
    delete (resman);
}
```

![test.gif](https://qiita-image-store.s3.amazonaws.com/0/153987/34975da0-cc13-a8b7-d883-d7bffb497baf.gif)

このように動けば完成です。
後は思う存分好きなアニメーションを作成して動かして見ましょう！

初めての執筆で粗い部分もありますが私の記事はここまでです。ありがとうございました。