Skip to main content
Coding Quests

Attack telegraphs

Is my enemy hard, or just unfair?

  • Combat
  • Beginner
  • 5 minutes
  • No account needed

An enemy hits you before you could possibly have reacted. It does not feel difficult. It feels unfair, because nothing told you it was coming. A short warning before each attack turns cheap hits into a fight you can learn.

Loading the lab...

No account needed. Nothing here is locked.

How it works

A telegraph is the gap between the warning and the hit. During it, the enemy holds still and shows what is coming. If the gap is longer than the player's reaction time, the attack is fair: they can see it and answer it. Make it shorter and only memorising the rhythm can save them.

Too much of a good thing

A windup that is too long makes enemies slow and harmless, and players stop respecting them. A tough enemy can wind up for less, as long as its warning is impossible to miss.

The usual mistake

A warning the player cannot see. A small flash on a busy screen, or a cue that plays off camera, gives them nothing to react to. The windup only counts if the warning is readable.

Use it when: Every attack that can hurt the player: enemy swings, boss patterns, traps and projectiles about to fire. The bigger the hit, the clearer the warning should be.

The code in Godot

The grunt's windup from the roguelike enemies quest: a state with a countdown and a red ring.

Show GruntEnemy.gd
GruntEnemy.gd
GDScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extends EnemyBase
​
@export var attack_range: float = 45.0
@export var telegraph_time: float = 0.35 # how long the red warning ring shows
​
func _ai_process(delta: float) -> void:
match state:
State.CHASE:
velocity = dir_to_player() * speed
if distance_to_player() <= attack_range and _cd <= 0.0:
_enter_telegraph()
State.TELEGRAPH:
# Hold still and count down. This is the player's window.
velocity = velocity.move_toward(Vector2.ZERO, 700 * delta)
state_timer -= delta
if state_timer <= 0.0:
_enter_attack()
​
func _enter_telegraph() -> void:
state = State.TELEGRAPH
state_timer = telegraph_time
show_telegraph_circle(attack_range * 0.85)

EnemyBase is the script every enemy in the quest inherits. It provides state, state_timer, dir_to_player() and the ring helper. The quest builds all of it, one piece at a time.

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 an attack telegraph?

A telegraph is a warning before an attack: a wind-up animation, a flash, a sound or a danger zone on the floor. It tells the player what is coming and gives them time to answer it.

How long should an enemy wind-up be?

Longer than the player's reaction time, which is about a quarter of a second for something they see. Basic enemies often sit between 0.3 and 0.6 seconds. Tough enemies go shorter with louder warnings.

How do I telegraph an attack in Godot 4?

Add a TELEGRAPH state between chasing and attacking. On entering it, start a timer and show a warning, like a red Polygon2D ring. When the timer reaches zero, hide the warning and attack.

Why do players call my enemies unfair?

Usually because an attack lands before they could react to it, or its warning was hard to see. Lengthen the wind-up or make the warning bigger before you make the enemy weaker.

The slime is a port of GruntEnemy.gd from the roguelike enemies quest: it chases at 95 px/s, winds up, swings for 0.14 s and recovers. The knight is the roguelike Player.gd with its 0.14 s dash. It steps at 60 frames a second. Art by Kenney, CC0.

Telegraph Enemy Attacks in Godot 4: Interactive Lab | Coding Quests