[Siv3D] 簡易的なスプライトシートアニメーションクラス
はじめに
Siv3Dには、アニメーション関連の機能が標準で存在しません。そこで、スプライトシートを使った簡易的なアニメーションクラスを作成しました。
スプライトシート?
Asepriteなどで簡単に作成することができます。動きごとに画像を並べ、切り出して使用します。
Asepriteを使ったスプライトシートの作成は、以下に詳しく書いてあります。
クラスの概要
スプライトシートからアニメーションを読み込み、管理するためのクラスです。スプライトシートから読み込んだアニメーションは、フレームごとの矩形領域として管理されます。
アニメーションは、特定の名前で識別され、各アニメーションは複数のフレームから構成されます。指定された速度で更新され、指定された位置と拡大率で描画されます。
本クラスのオブジェクトは、複数のアニメーションを持つことができます。それぞれのアニメーションは、それぞれ更新レートを設定できます。
また、アニメーションの切り替えやリセットが可能です。
メソッド一覧
void addAnimation(const String &animationName, int32 row, int32 frameCount, double rate = 0.5, bool loop = true): 新しいアニメーションを追加するメソッドです。アニメーション名、スプライトシート上の行、フレーム数、更新レートを指定します。デフォルトでは、ループするようになっています。
void updateAnimation(): アニメーションの更新を行うメソッドです。経過時間を管理し、指定された更新レートでフレームを切り替えます。
void draw(double scale = 1.0, Vec2 pos = Vec2{0, 0}) const: アニメーションを描画するメソッドです。指定した拡大率と位置でアニメーションを描画します。
void switchAnimation(const String &animationName): 現在のアニメーションを切り替えるメソッドです。指定されたアニメーション名のアニメーションに切り替えます。
void resetAnimation(): 現在のアニメーションのフレームをリセットするメソッドです。再生中のアニメーションのフレームを最初の状態に戻します。
メンバー変数一覧
std::map<String, Array<Rect>> animations: アニメーションを管理するためのマップです。キーにアニメーション名、値にそのアニメーションの各フレームの矩形領域を含む配列が格納されます。
std::map<String, int32> animationIndices: 各アニメーションの現在のフレームのインデックスを管理するマップです。キーにアニメーション名、値に現在のフレームのインデックスが格納されます。
std::map<String, double> animationRates: 各アニメーションの更新レート(フレームの切り替え速度)を管理するマップです。キーにアニメーション名、値に更新レートが格納されます。
std::map<String, double> animationTimers: 各アニメーションの経過時間を管理するマップです。キーにアニメーション名、値に経過時間が格納されます。
String currentAnimation: 現在再生中のアニメーションの名前を示す文字列です。
コード
#include <Siv3D.hpp> // Siv3D v0.6.13
class AnimatedObject
{
private:
Texture spriteSheet; // スプライトシート
Vec2 frameSize; // 一つのフレームの大きさ
std::map<String, Array<Rect>> animations; // アニメーションを管理するマップ
std::map<String, int32> animationIndices; // アニメーションの現在のインデックスを管理するマップ
std::map<String, double> animationRates; // アニメーションの更新レートを管理するマップ
std::map<String, double> animationTimers; // アニメーションの経過時間を管理するマップ
std::map<String, bool> loopFlags; // アニメーションのループフラグを管理するマップ
String currentAnimation; // 現在のアニメーションの名前
public:
// コンストラクタ
// @param spriteSheetPath スプライトシートのファイルパス
// @param frameSize 一つのフレームの大きさ
AnimatedObject(const String &spriteSheetPath, const Vec2 &frameSize)
: spriteSheet(Texture{spriteSheetPath}), frameSize(frameSize) {}
// アニメーションを追加するメソッド
// @param animationName 追加するアニメーションの名前
// @param row スプライトシート上のアニメーションが配置されている行
// @param frameCount アニメーションのフレーム数
// @param rate アニメーションの更新レート(省略可能、デフォルト値は0.5)
// @param loop アニメーションをループするかどうか(省略可能、デフォルト値は true)
void addAnimation(const String &animationName, int32 row, int32 frameCount, double rate = 0.5, bool loop = true)
{
Array<Rect> frames;
for (int32 i = 0; i < frameCount; ++i)
{
frames.push_back(Rect{static_cast<int32>(frameSize.x * i), static_cast<int32>(frameSize.y * row), static_cast<int32>(frameSize.x), static_cast<int32>(frameSize.y)});
}
animations[animationName] = frames;
animationIndices[animationName] = 0;
animationRates[animationName] = rate;
animationTimers[animationName] = 0.0;
loopFlags[animationName] = loop;
// 初期状態のアニメーションを設定
if (currentAnimation.isEmpty())
{
currentAnimation = animationName;
}
}
// アニメーションを更新するメソッド
void updateAnimation()
{
animationTimers[currentAnimation] += Scene::DeltaTime();
if (animationTimers[currentAnimation] > animationRates[currentAnimation])
{
if (animationIndices[currentAnimation] < animations[currentAnimation].size() - 1)
{
animationIndices[currentAnimation]++;
}
else
{
if (loopFlags[currentAnimation])
{
animationIndices[currentAnimation] = 0;
}
}
animationTimers[currentAnimation] = 0.0;
}
}
// アニメーションを描画するメソッド
// @param scale 描画時の拡大率(省略可能、デフォルト値は1.0)
// @param pos 描画位置(省略可能、デフォルト値は(0, 0))
void draw(double scale = 1.0, Vec2 pos = Vec2{0, 0}) const
{
const Rect ¤tFrame = animations.at(currentAnimation)[animationIndices.at(currentAnimation)];
spriteSheet(currentFrame).scaled(scale).drawAt(pos);
}
// アニメーションを切り替えるメソッド
// @param animationName 切り替えるアニメーションの名前
void switchAnimation(const String &animationName)
{
if (animations.count(animationName) && currentAnimation != animationName)
{
resetAnimation();
currentAnimation = animationName;
}
}
// アニメーションをリセットするメソッド
void resetAnimation()
{
animationIndices[currentAnimation] = 0;
animationTimers[currentAnimation] = 0.0;
}
};
使用例
#include <Siv3D.hpp>
void Main()
{
// スプライトシートのファイルパス
const String spriteSheetPath = U"assets/spritesheet.png";
// スプライトシート上の1フレームの大きさ
const Vec2 frameSize(64, 64);
// AnimatedObject クラスのインスタンスを生成
AnimatedObject animatedObject(spriteSheetPath, frameSize);
// アニメーションの追加
animatedObject.addAnimation(U"walk", 0, 4, 0.2);
animatedObject.addAnimation(U"idle", 1, 3, 0.3);
animatedObject.addAnimation(U"jump", 2, 2, 0.1);
// アニメーションの切り替え
animatedObject.switchAnimation(U"walk");
// ゲームループ
while (System::Update())
{
// アニメーションの更新
animatedObject.updateAnimation();
// アニメーションの描画
animatedObject.draw(Vec2(100, 100), 1.0);
}
}
参考
この記事が気に入ったらサポートをしてみませんか?