↓
iaen.es Devlog Leer en español

Gotic · 3 min read

A sprint boost with a stamina bar in Godot 4: threshold, grace period and stacking bonuses

One button that makes you 45 % faster on foot, swimming and on the broom, for as long as the bar lasts. It looked like the easiest feature in the game and still hid traps.

#mods#UI#testing#design

A sprint boost with a stamina bar in Godot 4: threshold, grace period and stacking bonuses

I wanted a sprint button with a kick, like in so many open-world games, but limited: something you feel and that runs out. That's the turbo mod. Press TURBO (or X on PC) and you move at ×1.45 on foot, swimming and flying on the broom. The camera's field of view widens by 8 degrees so you feel the speed, the button changes to ¡YA! ("go!") and a bar appears at the bottom that empties in 4 seconds. It switches off when the bar runs dry, when you stop, or when you press again; a second later the bar starts refilling and takes 7 seconds to fill up.

Measured with the player's physics in the tests: 8.7 m/s versus 6.0, it lasts 4.0 s and can be used again 3.1 s after running out.

How it's built

The turbo doesn't move the player. It uses the bonuses the game already offers any mod: speed_factors (on foot), swim_factors (swimming) and fly_factors (read by the broom). It sets its own named factor and removes it when switching off, so it stacks with herbal infusions, cooked dishes and the boots without knowing about them, within the usual caps (each bonus between 0.5 and 1.5). The boat doesn't use bonuses, so the turbo switches off on board.

The whole clock lives in one function the tests can call directly, without waiting for frames:

func advance(delta: float) -> void:
	if active:
		stamina = maxf(stamina - delta / EMPTY_SECONDS, 0.0)
		var moving := Vector2(world.player.velocity.x, world.player.velocity.z).length() > 0.5 or world.player.mover != null
		_still = 0.0 if moving else _still + delta
		if stamina <= 0.0 or _still >= STILL_SECONDS or not _moves_with_factors():
			stop()
	else:
		_since_stop += delta
		if _since_stop >= REFILL_DELAY:
			stamina = minf(stamina + delta / REFILL_SECONDS, 1.0)
	_paint()

The bar is a Control that draws itself: amber when usable, red when not there yet, lighter while active. It only shows when it isn't full, so it doesn't take up screen space for nothing.

What went wrong

  • With a nearly empty bar, it would flicker. If it could start on any leftover, it would switch on and off with every step. That's why it won't start below 30 %: it says there's no stamina left and plays an error sound.
  • Started while standing, it switched off before you got going. The "stopped for 0.4 s, switch off" rule fired before the player had started moving. The screenshots exposed it: they run at a few frames per second, which stretches every step. Now, if you start it standing still, you get one second of grace.
  • The button covered the character. It started next to them, in the middle of the screen. It moved to the top right, next to VISTA (camera distance).

Mutations the tests caught: switching the mod off without removing the boost, no threshold, not switching off when stopped, the clock running with the menu open, starting it in the boat, and refilling without the delay. With the turbo, the character editor and the inventory, the suite reached 1,253 checks.

Still unverified

I haven't tried it on a phone, or with a real thumb. The numbers (×1.45, 4 s, 7 s, 30 %) are my own choice: I don't know whether a player will find it too short or too generous.

More from Gotic →