fix: SpeedRunHud never detached from the autoload's signal

every level load left another handler attached to a freed HUD.
SpeedRunManager outlives the scene, so it kept calling them and each one
wrote to a disposed Label - thousands of ObjectDisposedException per
frame, growing with each load.

Latent all along: StartTimer was only ever called from StartNewGame, so
after a Continue the timer never ticked and the dead handlers never ran.
Calling StartTimer on Continue (needed for splits and best times) made
it visible.

Also stop the timer when leaving to the menu - StopTimer existed and had
no callers, so the run kept ticking in the main menu.
This commit is contained in:
2026-07-28 20:00:55 +02:00
parent a1c9042652
commit f4411e05a3
3 changed files with 20 additions and 3 deletions

View File

@@ -39,6 +39,7 @@ public partial class GameOverScreen : Control
private void OnMainMenuClicked() private void OnMainMenuClicked()
{ {
GameStateStore.Instance?.ResetAll(); GameStateStore.Instance?.ResetAll();
SpeedRunManager.Instance?.StopTimer();
GetTree().ChangeSceneToPacked(MainMenuScene); GetTree().ChangeSceneToPacked(MainMenuScene);
} }

View File

@@ -84,6 +84,8 @@ public partial class PauseMenu : Control
{ {
GameManager.ResumeGame(); GameManager.ResumeGame();
GameStateStore.Instance?.ResetSession(); GameStateStore.Instance?.ResetSession();
// The run is over once you leave to the menu; otherwise the timer keeps ticking there.
SpeedRunManager.Instance?.StopTimer();
GetTree().ChangeSceneToPacked(MainMenuScene); GetTree().ChangeSceneToPacked(MainMenuScene);
} }

View File

@@ -1,5 +1,4 @@
using Godot; using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads; using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI; namespace Mr.BrickAdventures.scripts.UI;
@@ -11,11 +10,26 @@ public partial class SpeedRunHud : Control
public override void _Ready() public override void _Ready()
{ {
if (SpeedRunManager.Instance != null)
SpeedRunManager.Instance.TimeUpdated += OnTimerUpdated; SpeedRunManager.Instance.TimeUpdated += OnTimerUpdated;
} }
/// <summary>
/// Detaches from the autoload's signal. Without this, every level load left another handler
/// attached to a freed HUD: SpeedRunManager outlives the scene, so it kept calling them and each
/// one touched a disposed Label. It stayed hidden only because the timer was never started on
/// Continue, so the signal never fired outside a fresh run.
/// </summary>
public override void _ExitTree()
{
if (SpeedRunManager.Instance != null)
SpeedRunManager.Instance.TimeUpdated -= OnTimerUpdated;
}
private void OnTimerUpdated(double totalTime, double levelTime) private void OnTimerUpdated(double totalTime, double levelTime)
{ {
if (_timerLabel == null || !IsInstanceValid(_timerLabel)) return;
_timerLabel.Text = SpeedRunManager.FormatTime(totalTime); _timerLabel.Text = SpeedRunManager.FormatTime(totalTime);
} }
} }