手付けUIアニメーションのすすめ

始めまして。入社3年目のプログラマー J と申します。

キャッシュを考慮したコードを書いてみよう!を書いた人の同期です。

今回は、手付けで実装する簡単なUIのプログラムを紹介したいと思います。
UIを手付けで実装するメリットは、性能が低いハードでのFPS保持や、複雑・オシャレな動きをさせたいなどがあります。
デメリットとしては、プログラマーの工数が増えるのでスケジュールとの相談だったりです。

使用したライブラリはSiv3Dです。
お手軽にプログラムが書けるので個人的におススメです。

まずは四角形を1つ表示してみます。

// ---------------------------------------- include
# include <Siv3D.hpp>

// ---------------------------------------- function
void Main()
{
	// ウィンドウのタイトルを設定する
	Window::SetTitle(L"UI Sample");

	while (System::Update())
	{
		Rect(Point(100, 100), 150, 50).draw({ 0, 255, 0, 255 });
	}
}

次にUIに必要なインアニメ、アウトアニメ、待ちアニメを制御する処理を追加します。

// ---------------------------------------- include
# include <Siv3D.hpp>

// ---------------------------------------- enum
// 状態の種類
enum eState
{
	eState_In,  // インアニメ中
	eState_Out, // アウトアニメ中
	eState_Wait,// 待機アニメ中
	eState_Num, // 最大値
};

// ---------------------------------------- static
// 現在の状態
static eState s_State = eState_Wait;
// 描画フラグ
static bool s_IsDisp = false;

// ---------------------------------------- function
// 描画関数
static void _Draw()
{
	switch (s_State)
	{
		// インアニメ
	case eState_In:
		break;
		// アウトアニメ
	case eState_Out:
		break;
		// 待ちアニメ
	case eState_Wait:
		break;
	}

	Rect(Point(100, 100), 150, 50).draw({ 0, 255, 0, 255 });
}

// メイン関数
void Main()
{
	// ウィンドウのタイトルを設定する
	Window::SetTitle(L"UI Sample");

	while (System::Update())
	{
		if (s_IsDisp)
		{
			_Draw();
		}
	}
}

状態の種類、現在の状態、描画フラグを定義し、更新処理をswitchで分けるようにしました。
このコードを実行すると s_IsDisp が false のためUI表示されません。

それでは、
・左クリックしたら状態を eState_In にしてUIを表示
・インアニメ中はα値を60フレームで0から255に変化
・右クリックしたら状態を eState_Out にする
・アウトアニメ中はα値を30フレームで255から0に変化してUIを非表示
となるように実装します。

// ---------------------------------------- include
# include <Siv3D.hpp>

// ---------------------------------------- enum
// 状態の種類
enum eState
{
	eState_In,  // インアニメ中
	eState_Out, // アウトアニメ中
	eState_Wait,// 待機アニメ中
	eState_Num, // 最大値
};

// ---------------------------------------- static
static eState s_State = eState_Wait;   // 現在の状態
static bool s_IsDisp = false;		   // 描画フラグ
static int s_Timer = 0;				 // アニメ用タイマー

// ---------------------------------------- const
static const int ALPHA_MAX = 255;	   // α値の最大値
static const int IN_ANIM_TIME = 60; // インアニメの時間
static const int OUT_ANIM_TIME = 30;	// アウトアニメの時間

// ---------------------------------------- function
// 描画関数
static void _Draw()
{
	int alpha = ALPHA_MAX; // α値

	switch (s_State)
	{
		// インアニメ
	case eState_In:
	{
		double rate = static_cast<double>(s_Timer) / IN_ANIM_TIME;
		alpha = static_cast<int>(rate * ALPHA_MAX);

		// アニメ終了チェック、終了したら待ちアニメ状態にする
		if (++s_Timer >= IN_ANIM_TIME)
		{
			s_State = eState_Wait;
		}
	}
		break;
		// アウトアニメ
	case eState_Out:
	{
		double rate = static_cast<double>(OUT_ANIM_TIME - s_Timer) / OUT_ANIM_TIME;
		alpha = static_cast<int>(rate * ALPHA_MAX);

		// アニメ終了チェック、終了したらUIを非表示にする
		if (++s_Timer >= OUT_ANIM_TIME)
		{
			s_IsDisp = false;
		}
	}
		break;
		// 待ちアニメ
	case eState_Wait:
		// 何もしない
		break;
	}

	Rect(Point(100, 100), 150, 50).draw({ 0, 255, 0, alpha });
}

// メイン関数
void Main()
{
	// ウィンドウのタイトルを設定する
	Window::SetTitle(L"UI Sample");

	while (System::Update())
	{
		// 左クリック
		if (Input::MouseL.clicked)
		{
			s_State = eState_In;
			s_IsDisp = true;
			s_Timer = 0;
		}
		// 右クリック
		else if (Input::MouseR.clicked)
		{
			s_State = eState_Out;
			s_IsDisp = true;
			s_Timer = 0;
		}

		if (s_IsDisp)
		{
			_Draw();
		}
	}
}

特に難しいことはせず、最終的なパラメータに対してオフセットするような形です。

これで基本部分の実装はできました。

あとはアニメーションの補間にイージングを使用し、移動、拡縮、回転などを加えるとクオリティアップできます。

また、状態を増やすことでイン、アウトだけでなく、別の動きを追加することも簡単です。

最後に

やはり、手付けでアニメーションを実装するのは時間がかかるものの、見た目が良く、処理速度が速いので、時間があるときは是非お試しください。

それでは。

Follow me!