表紙の写真

スプライトを描く(3)

さっそく作成したソフトウェアスプライトクラス、あるいはソフトウェアスプライト型を使って実際の描画をしてみましょう。

今回作るのは、うにょうにょが気持ちいいコレです。

スプライトデータは、うにょうにょ部分のみを切り出して作成しました。



タスク管理システムの導入

OpenSiv3Dのことですから、何かしらキャラクタ管理を簡単にするクラスが実装されているに違いない。そう思って調べてみるとOpenSiv3Dと親和性の高いタスク管理システムが用意されていました。今回はこれを使ってみます。

https://github.com/Rinifisu/TaskSystem

githubからソースコードをダウンロードをしたら、次のいずれかの手段で追加します。

(1) 丸ごとインポートする
(2) 追加ディレクトリを指定する

ヘッダファイルのみで実装されているので、どちらの手段を使っても簡単に設定できます。

ダウンロードしたソースコードは、これから何度も使い回すことを考えて、OpenSiv3Dがインストールされたディレクトリのinclude/ThirdParty配下にコピーしておくのがよいと思います。

OpenSiv3Dがインストールされたディレクトリは環境変数SIV3D_ほにゃららに設定されています。たとえば、バージョン0.3.1の場合なら環境変数名はSIV3D_0_3_1となっています。

丸ごとインポートする

丸ごとインポートすると設定いらずで簡単です。現在作成しているプロジェクトを開いてTaskSystemフォルダごと追加します。

追加ディレクトリを指定する

コンパイラオプションに追加インクルードディレクトリの場所を記述します。


タスクの生成

タスクシステムを作成したのは、Rinifisuさんという方で、Qiitaでタスクシステムのページをポストされています。

https://qiita.com/Rinifisu

このタスクシステムをウェブ検索で見つけてから導入~実装まで15分程度しか掛かりませんでした。OpenSiv3D + rnfs::Taskでゲーム開発もここまで簡単になるのか!とビックリしました。

Taskクラスを派生させて独自のオプションタスクを作成します。コンストラクタでタスクの寿命を指定するのと、更新時に呼び出されるコールメソッドを指定することが必須で、あとは自由に作れるっぽいです。

次のソースコードはオプションタスクの実装例です。タスクの寿命は60フレームに設定しました。

class Option : public Task {
private:
   Vec2 m_Pos;
   TaskCall m_Update;
   Array<SoftwareSpriteT>& m_Sprite;
   int m_Framecounter;
public:
   Option(Array<SoftwareSpriteT>& option) : Task(TaskDestroyMode::Count, 60)
       , m_Pos(Cursor::Pos())
       , m_Update(this, &Option::Update)
       , m_Sprite(option)
       , m_Framecounter(0)
   {}
private:
   void Update()
   {
       static const int animation[] = { 0, 1, 2, 1 };
       m_Sprite.at(animation[(m_Framecounter++ >> 3) & 3]).draw(m_Pos);
   }
};

タスクの生成はCreate<T as Task>メソッドを呼び出します。タスクを生成すれば自動的にタスク管理が始まります。これがとても便利です。


タスクの更新

Create<T as Task>メソッドで生成したタスクは、自動的にタスク管理されますから、Task::All::Updateメソッドを呼び出すだけで、生成されたすべてのタスクが実行されます。

Task::All::Updateメソッドを呼び出した際、タスクに設定された寿命が尽きると自動的に消滅してくれます。もちろん、寿命をプログラマがコントロールすることもできます。プログラマが寿命をコントロールしたい場合は、コンストラクタでは寿命を記述せず、Destoryメソッドを呼び出します。

注意が必要なのは、Task:All::Updateメソッドはタスク内部のプロパティ更新しか行わない点です。

Taskクラスから派生したクラスを記述した際に設定したコールメソッドを呼び出すのはTaskCall::All:Updateメソッドです。後者のTaskCall::All:Updateメソッドを呼び出すのを忘れてしまうと、せっかく実装した独自ロジックが実行されません。

ですから、タスクを更新する場合は、次のように記述することを忘れないようにしましょう。

TaskCall::All::Update();
Task::All::Update();


Arrayテンプレートの利用

OpenSiv3Dには独自の拡張コンテナArrayがあることを知りました。Arrayコンテナを使っておくと、これまたOpenSiv3Dで拡張したfor文が使いやすいようなのでArrayコンテナを利用します。

Arrayコンテナについてはまだ勉強していないのでまた実装をしながら採り入れていきます。


完成したソースコード

タスクシステムを使ったソフトウェアスプライトの描画サンプルです。これだけ短いソースコードでここまでできるのは楽しいですね。

#include <Siv3D.hpp>
#include <rnfs.h>

typedef TextureRegion SoftwareSpriteT;

class Option : public Task {
private:
   Vec2 m_Pos;
   TaskCall m_Update;
   Array<SoftwareSpriteT>& m_Sprite;
   int m_Framecounter;
public:
   Option(Array<SoftwareSpriteT>& option) : Task(TaskDestroyMode::Count, 60)
       , m_Pos(Cursor::Pos())
       , m_Update(this, &Option::Update)
       , m_Sprite(option)
       , m_Framecounter(0)
   {}
private:
   void Update()
   {
       static const int animation[] = { 0, 1, 2, 1 };
       m_Sprite.at(animation[(m_Framecounter++ >> 3) & 3]).draw(m_Pos);
   }
};

void Main()
{
   Texture texPlayer(U"option.png");
   Array<SoftwareSpriteT> unyounyo;
   for (int x : {0, 1, 2}) {
       unyounyo.push_back(texPlayer(x * 16, 0, 16, 16));
   }
   while (System::Update())
   {
       Create<Option>(unyounyo);
       TaskCall::All::Update();
       Task::All::Update();
   }
   Task::All::Clear();
}

この記事が気に入ったらサポートをしてみませんか?