【Godot4】TextureProgressBarが早く減る!stepの初期値が罠だった

2026-11-13

Godotで時間とともに減るゲージを作ったところ、同じコードでもTextureProgressBarだけ想定より早く減ってしまいました。

本記事では、stepの初期値による丸めが原因であることを、ProgressBarとの比較と実行ログで確認し、対処法を紹介します。

環境

  • Godot4.7
  • GDScript

具体例

  • Control
    • ProgressBar
    • TextureProgressBar

上記のようなシーンを作成し、適切なsizeとpositionを設定します。

TextureProgressBarには任意の画像を設定してください。 TexturesのProgressだけ設定すれば最低限動きます。

GodotのインスペクターでTextureProgressBarのTexturesのProgressに黄色いゲージ画像を設定した画面
GodotのインスペクターでTextureProgressBarのTexturesのProgressに黄色いゲージ画像を設定した画面

シーンを実行したら二つのバーを最大にします。 その後、3秒間かけてvalueを減らしていきます。

スクリプトは親のControlに設定します。

extends Control

@onready var progress_bar := $ProgressBar
@onready var texture_progress_bar := $TextureProgressBar

func _ready() -> void:
	progress_bar.value = progress_bar.max_value
	texture_progress_bar.value = texture_progress_bar.max_value

func _process(delta: float) -> void:
	progress_bar.value -= progress_bar.max_value * delta / 3.0
	texture_progress_bar.value -= texture_progress_bar.max_value * delta / 3.0

動画では、上に配置したProgressBarが3秒かけてだんだんゲージが減っていたのに対して、 下に配置したTextureProgressBarが3秒より早くなくなってしまいました。

⚠️注意 TextureProgressBarが減らない場合

高FPSの環境では、1フレームの減算量が小さくなり、丸めによって値が変化しない場合があります。

本記事では「想定より早く減る現象」を再現するため、プロジェクト設定から最大fpsを60に制限してください。

その際にプロジェクト設定の高度な設定をONにしないと最大fpsを設定できないので注意してください。

Godotのプロジェクト設定で、右上の「高度な設定」スイッチの位置を赤丸で示した画面
Godotのプロジェクト設定で、右上の「高度な設定」スイッチの位置を赤丸で示した画面
Godotのプロジェクト設定の「アプリ→実行」で、最大FPSを60に設定した画面
Godotのプロジェクト設定の「アプリ→実行」で、最大FPSを60に設定した画面

問題点

先ほどのように3秒間でなくなるはずが、TextureProgressBarが3秒より早くなくなってしまいます。

これはTextureProgressBarが継承しているRangeのStepの値に原因があります。

Stepは0より大きい時、valueの値はmin_value+N*stepの形で表される値に丸められます。 例:min_valueが0.1でstepが0.2の場合、valueは 0.1、0.3、0.5 のような値をとります。

ProgressBarでの初期値は0.01でTextureProgressBarでの初期値は1.0です。

このstepの影響を調べたいと思います。

先ほどのプログラムの_process()を以下のように変更します。

func _process(delta: float) -> void:
	print("delta: ", delta)
	print("sub value: ", texture_progress_bar.max_value * delta / 3.0)
	print("value: ", texture_progress_bar.value - (texture_progress_bar.max_value * delta / 3.0))
	
	progress_bar.value -= progress_bar.max_value * delta / 3.0
	texture_progress_bar.value -= texture_progress_bar.max_value * delta / 3.0
	
	print("true value: ", texture_progress_bar.value)

4つのprint命令を追加しました。 それぞれ、deltaは経過時間、sub valueはその_process()でtexture_progress_bar.valueを減らす値、valueはstepの処理が入る前の値、true valueはstepの処理が行われた後の値を表しています。

実行時のログの一部を次の表に示します。

deltasub valuevaluetrue value
0.016666666666670.5555555555555640.444444444444440.0
0.016666666666670.5555555555555639.444444444444439.0
0.020717333333330.6905777777777838.309422222222238.0

deltaには前のフレームからの経過時間が入るので多少の変動があるが大体1/60秒です。 stepが0より大きい場合、代入した値はstepで決まる刻みに丸められます。今回の step は 1.0 なので、計算結果の 39.444… が 39.0 になります。毎フレーム、この丸め後の値から再び減算するため、誤差が積み重なります。

実際に減らさなければならない値は0.555程度なのに40.0から39.0のように1.0も減らしてしまっています。 この誤差が積み重なり、想定した3秒より早く減ってしまっていました。

解決方法

大きく分けて二つの解決方法があります。

stepの値を小さくする

足し引きする値に対してstepが大きいので誤差が大きくなっています。

なので誤差が気にならない程度にstepの値を小さくすると、丸めによる誤差を抑えられます。

TextureProgressBarのstepを0.01にした結果です。

経過時間から計算して、丸め誤差の蓄積を防ぐ

stepの誤差が入っているvalueを使用しているのが問題なのでvalueを使わない計算方法にしましょう。

extends Control

@onready var progress_bar := $ProgressBar
@onready var texture_progress_bar := $TextureProgressBar

var elapsed_time = 0.0

func _ready() -> void:
	elapsed_time = 0.0
	progress_bar.value = progress_bar.max_value
	texture_progress_bar.value = texture_progress_bar.max_value

func _process(delta: float) -> void:
	elapsed_time += delta
	
	progress_bar.value -= progress_bar.max_value * delta / 3.0
	texture_progress_bar.value = texture_progress_bar.max_value - (texture_progress_bar.max_value * elapsed_time / 3.0)

スタートから経過した時間を記録するelapsed_timeを新たに追加し、それを使って値を計算します。

この方法でも表示値はstep単位に丸められますが、その値を次の計算に使わないため、誤差が積み重なりません。

まとめ

TextureProgressBarのstepの初期値は1.0なので誤差が大きく出てしまう可能性がある。

stepを小さくして丸めによる誤差を抑えるか、経過時間から値を計算して丸め誤差の蓄積を防げば良い💯

ブログゲーム開発Godot