スライダー


概要

スライダーUIのゲージなどで使用する手法です。
この手法では「最小値」と「最大値」と「スライダーの値」の三つの情報から
比率を算出して、下の絵のように比率によってサイズを変更させて描画します。
※スライダーの値は最小値と最大値の範囲内でスライダーが
 どの位置にいるかを示すための値です。

2d_game_0010

スライダーは「比率」による「描画サイズの変更」さえできれば実装可能です。
さらに汎用的にするために「一定フレームを掛けて増減」させたり、
「方向情報を付与」して右から左に増減する、
下から上に増減するといったことを出来るようにします。

サンプル

サンプルはここからダウンロードでき、環境については以下の内容となっています。

開発環境
VSのバージョン DirectXのバージョン
VisualStudio 2019 DirectX9

比率計算

比率計算は以下の式を実装するだけなので、全く難しくありません。

// 式
比率 = (スライダーの値 - 最小値) / (最大値 - 最小値)

// 具体例
float rate = (slider.CurrentValue - slider.MinValue) / (slider.MaxValue - slider.MinValue);

比率を出す際に「スライダーの値 / 最大値」で行っていないのは
最小値に0、最大値に0以上の値が設定される保証がないからです。
例えば以下の値を「スライダーの値 / 最大値」で求めても正しい値になりません。

最小値 最大値 スライダーの値
-10 10 2
間違った答え スライダーの値 / 最大値 = 比率 0 / 10 = 0.1 このようにならないように最小値をスライダーの値と最小値から引きます。 正しい答え (スライダーの値 - 最小値) / (最大値 - 最小値) = 比率 (2 - (-10)) / (10 - (-10)) = 0.6

描画サイズの変更

比率が決まったら描画時のサイズに反映します。
描画する矩形のサイズは分かっていると思いますので、
そちらに比率を掛けてサイズを変更してください。
※サンプルでは原点は矩形の左上頂点を使用しています。

// スライダーの値を比率として算出する
float rate = (slider.CurrentValue - slider.MinValue) / (slider.MaxValue - slider.MinValue);

// 横幅に比率を掛けてサイズを調整する
width *= rate;

// 描画
DrawRect(
	pos_x,
	pos_y,
	width,
	height
);

2d_game_0011

結果は上の絵の通りです。
きちんと描画比率に合わせて描画ができているので、ひとまずは完成といえます。

テクスチャマッピングにも反映する

このページの一番上の絵ではテクスチャマッピングを行っていますが、
すぐ上の絵では行っていません。
これは何も対策を行わずにテクスチャマッピングをすると
内容がおかしくなる可能性があるからです。

2d_game_0012

上の絵の比較なら25%と50%が分かりやすくなっています。
対策を行わないとサイズが小さくなった矩形にそのままテクスチャマッピングされるので
マッピングしたテクスチャがつぶれたように描画されます。
スライダーで使用されるテクスチャは模様などが入ってることが多いので、
つぶれたままで描画するとグラフィッカーの方に失礼です。
なので、テクスチャマッピングを必要する場合はきちんと対策をしましょう。

対策

対策方法はシンプルでテクスチャマッピングで指定している
サイズにも比率を掛けるだけです。

// 横幅に比率を掛けてサイズを調整する
tex_width *= rate;

こうすれば、矩形のサイズに合わせてマッピングするサイズも変化するので、
きちんとした範囲でテクスチャマッピングが行われます。
※この処理の結果は対策済みの絵と同じ内容になります。

一定フレームを掛けて増減

今の実装状態では現在の値を変更した場合、
即座にスライダーの値が変更した場所に描画されますが
よくあるスライダーの動きではスライダーの値が変更された場合、
数フレームかけてスライダーの値が目的の値まで変化するようになっています。



上の動画で上が即時変更、下が一定フレームをかけてスライダーの値を変更しています。
仕様によっては下の挙動が必要となると思いますので、こちらの解説をします。

実装方法

実装は「次のスライダーの値」を保存する変数を用意します。
(サンプルではNextValueがそれにあたります)
そして、毎フレームスライダーの値が異なったらその位置に到達するように
今のスライダーの値を変化させるだけです。

// 次のスライダーの値を保存
void UpdateSliderNextValue(float next_value, Slider& out_slider)
{
	// 値を更新する
	out_slider.NextValue = max(out_slider.MinValue, min(next_value, out_slider.MaxValue));
}

// スライダーの値を更新(毎フレーム実行)
void UpdateSliderCurrentValue(Slider& out_slider)
{
	// NextValueとCurrentValueに差があればMoveSpeedで演算する
	if (out_slider.CurrentValue <= out_slider.NextValue)
	{
		out_slider.CurrentValue = min(out_slider.CurrentValue + out_slider.MoveSpeed, out_slider.NextValue);
	}
	else
	{
		out_slider.CurrentValue = max(out_slider.CurrentValue - out_slider.MoveSpeed, out_slider.NextValue);
	}
}

これでスライダーの値が変更と同時に更新した位置にならずに、
一定の時間をかけて変化していくことになります。

速度

スライダーの速度の設定は以下のような方法があるので、仕様に合わせて実装してください。

方法
スライダーが動く速度を決めて、その速度で移動させる
時間を決めて、その時間に対しての速度を求めて移動させる
上記のどちらかの方法を加速、または減速しながら移動させる
今回のサンプルは「②」で実装しています。 void UpdateSliderNextValue(float next_value, Slider& out_slider) { // 値を更新する out_slider.NextValue = max(out_slider.MinValue, min(next_value, out_slider.MaxValue)); // 今の値と新しい値の差を出して、速度を算出する float distance = fabsf(out_slider.CurrentValue - out_slider.NextValue); // 移動にかかるフレーム数 float moev_frame = 60.0f; // 距離をフレーム数で割って速度を求める out_slider.MoveSpeed = distance / moev_frame; } これで速度も決まったので「一定フレームを掛けて増減」の実装は終了です。

方向情報を付与

スライダーの移動方向は以下の4通りが考えられます。

移動方向
 左から右 
 右から左 
 上から下
 下から上 
現状の実装は左から右のみ実装していますが、 これだけでは使い勝手が良くないので、残りの方向の実装について説明します。 今回のポイントはサイズが変わることで原点である座標に変更が必要かどうかです。 ※実装する際はSlider用の構造体などに「進行方向」が分かる変数を用意してください。

原点座標の変更が必要ない

今回のサンプルの原点は左上座標で行っているため、
「左から右」と「上から下」の二つはサイズが変化しても原点座標は変更されません。

2d_game_0013

上の絵で示しているようにサイズが変更して影響があるのは右辺のみなので、
描画の際に右辺、下辺に比率を掛ける以外で何か特別な変更を加える必要はありません。
※原点が左上以外の場合は別途処理を加える必要があります。

下辺の対応ですが、こちらもY軸の矩形やマッピング範囲のサイズに対して
比率を掛けるだけで対応可能です。

// 縦幅に比率を掛けてサイズを調整する
height *= rate;

原点座標の変更が必要

左上が原点の場合「左から右」「上から下」は右辺または下辺が変動しますが
「右から左」「下から上」は左辺や上辺の変更が必要となります。
これは原点(描画座標)の変更を行う必要があるということなので、
以下の式のような式を使用して原点を変更しなければいけません。

番号
オフセット値 = 右辺のX座標 - (サイズ * 比率)
オフセット値 = 左辺のX座標 - (サイズ * (1.0 - 比率))
以下の絵はこの式を使用して左辺の座標を求めています。 2d_game_0014 どちらの式を使用しても左辺の座標は変わりませんが、 ②の式の方が左辺の座標と比率だけで対応可能なので、 右辺の座標を求める必要がある①よりもシンプルだと思います。 ※サンプルでは②を使用しています。 // テクスチャの座標も比率の分だけずらす out_tex_pos += (1.0f - rate) * size; 下辺から上辺も以下のように座標をY座標で行えば対応可能です。
番号
オフセット値 = 下辺のY座標 - (サイズ * 比率)
オフセット値 = 上辺のY座標 - (サイズ * (1.0 - 比率))
これで「進行方向付与」の実装も完了です。