Skip to main content
Coding Quests

Hit-stop

Why do my hits feel weightless?

  • Combat
  • Beginner
  • 4 minutes
  • No account needed

Your sword connects, the number pops up, and the hit still feels like nothing. The blade passes the target in a single frame. Your eye never gets a moment to register that anything landed.

Loading the lab...

No account needed. Nothing here is locked.

How it works

Hit-stop holds the game still for a few frames at the moment of impact. Nothing about the damage changes. What changes is that your eye gets time to read the hit, so it reads as force. It is the cheapest weight you can add to combat.

Too much of a good thing

Freeze too long and hits feel like the game is stuttering. Fast combos turn sticky, because every hit stops the flow. Keep quick hits short and save the long freeze for big ones.

The usual mistake

Waiting on a normal timer while time is frozen. That timer slows down too, so the freeze lasts far longer than you meant. Pass true as the fourth argument of create_timer, so it ignores the time scale.

Use it when: Any hit you want the player to feel: sword strikes, parries, blocks, a boss taking damage. Many games scale it with the hit, so a finisher freezes longer than a jab.

The code in Godot

The roguelike quest's hit_stop, in its GameManager autoload, with its two lengths.

Show GameManager.gd
GameManager.gd
GDScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extends Node
​
const QUICK_HITSTOP := 0.04
const HEAVY_HITSTOP := 0.07
​
## Freeze the whole game for a few real-time milliseconds to sell an impact.
func hit_stop(duration: float) -> void:
if duration <= 0.0:
return
# Not 0.0: a zero physics step breaks bodies standing on moving platforms.
Engine.time_scale = 0.02
# The 4th argument, ignore_time_scale, keeps this timer on real seconds.
var timer := get_tree().create_timer(duration, true, false, true)
await timer.timeout
Engine.time_scale = 1.0

An autoload is a script Godot keeps loaded the whole time, so any scene can call GameManager.hit_stop(). Call it with QUICK_HITSTOP when a quick swing lands, and HEAVY_HITSTOP for a heavy one.

Build it for real

A lab shows you the decision. A quest has you write the whole system, with every step checked as you go.

Try another lab

All labs

Questions

What is hit-stop in games?

Hit-stop is a very short freeze when an attack lands. The game holds still for a few frames, then carries on. It makes hits feel heavy without changing any damage numbers.

How long should hit-stop be?

About 0.03 to 0.1 seconds for most hits, which is 2 to 6 frames at 60 frames per second. Big finishing blows can go a little longer. Past about 0.15 seconds it starts to read as lag.

Why does my Godot game freeze for too long after a hit-stop?

Because the timer you are waiting on is slowed down by Engine.time_scale too. Create it with get_tree().create_timer(duration, true, false, true). The fourth argument makes it ignore the time scale.

Should hit-stop freeze the whole game?

Freezing everything is the simple version, and the one the roguelike quest uses. Many action games freeze only the two fighters and let sparks and the camera keep moving. The Game Feel Lab lets you compare both.

The arena is a port of the roguelike quests' Player.gd and WeaponController.gd: a 0.10 s swing, ready again after 0.35 s. Like the quest's hit_stop, the freeze stops the whole game, sparks included. It steps at 60 frames a second. Art by Kenney, CC0.

Hit-Stop in Godot 4: Feel the Freeze Frame | Coding Quests