Hitscan
An attack resolved by tracing a line the moment it fires, so it hits at once with no projectile to dodge.
On this page
A hitscan attack traces a line from the shooter when it fires and damages the first thing in the way, in the same tic. In Doom, bullets and melee trace lines, 2,048 units for guns, 64 for melee1, and rockets, plasma, and BFG balls are objects that fly at a fixed speed1. The shotgun fans 7 traces at once2.
Wolfenstein 3D traces no path. Its gun shot picks the nearest visible target near the center of the screen and hits it if the line to it is clear3. An enemy’s shot is a roll against the distance, with lower odds when the player runs4.
- Target pickWolfenstein 3D (1992). A gun shot picks the nearest visible target near the screen's center and checks a clear line to it, and an enemy's shot is a roll against the distance and the player's speed.3,4,5
- Hitscan beside projectilesDoom (1993). Bullets and melee trace a line in the tic they fire; rockets, plasma, and BFG balls fly.1,2Quake II (1997). Bullets, pellets, and the railgun trace a line when fired, the railgun through every body in its path; the blaster, grenades, and rockets fly.6
Games
| Game | Year | How | Note |
|---|---|---|---|
| Wolfenstein 3D | 1992 | Target pick | a gun shot picks the nearest visible target near the screen's center and checks a clear line to it, and an enemy's shot is a roll against the distance and the player's speed |
| Doom | 1993 | Hitscan beside projectiles | bullets and melee trace a line in the tic they fire; rockets, plasma, and BFG balls fly |
| Duke Nukem 3D | 1996 | Hitscan beside projectiles | the pistol, shotgun, and chaingun trace a line in the call that fires them; the RPG fires a rocket sprite with a speed of its own |
| Quake II | 1997 | Hitscan beside projectiles | bullets, pellets, and the railgun trace a line when fired, the railgun through every body in its path; the blaster, grenades, and rockets fly |
Every claim about this mechanic, by game (8)
References
- ^ abcDoom's bullets and melee are hitscan:
P_LineAttacktraces a line in the same tic, out to 2,048 units for guns and 64 for melee, and damages the first shootable thing whose height spans the shot's slope; rockets, plasma, and BFG balls are objects spawned byP_SpawnPlayerMissilethat move each tic at their type's speed (20, 25, and 25 units per tic).Codeid-Software/DOOMrevisedHow we know
483 slope = P_AimLineAttack (player->mo, angle, MELEERANGE); 484 P_LineAttack (player->mo, angle, MELEERANGE, slope, damage);
linuxdoom-1.10/p_pspr.c lines 483–484 at a77dfb9 · upstream ↗ 57#define MELEERANGE (64*FRACUNIT) 58#define MISSILERANGE (32*64*FRACUNIT)
linuxdoom-1.10/p_local.h lines 57–58 at a77dfb9 · upstream ↗ 1063P_LineAttack ⋮ 1082 P_PathTraverse ( t1->x, t1->y, 1083 x2, y2, 1084 PT_ADDLINES|PT_ADDTHINGS, 1085 PTR_ShootTraverse );
linuxdoom-1.10/p_map.c lines 1063–1085 at a77dfb9 · upstream ↗ 984 thingtopslope = FixedDiv (th->z+th->height - shootz , dist); ⋮ 986 if (thingtopslope < aimslope) 987 return true; // shot over the thing ⋮ 1010 if (la_damage) 1011 P_DamageMobj (th, shootthing, shootthing, la_damage); ⋮ 1013 // don't go any farther 1014 return false;
linuxdoom-1.10/p_map.c lines 984–1014 at a77dfb9 · upstream ↗ 556 P_SpawnPlayerMissile (player->mo, MT_ROCKET);linuxdoom-1.10/p_pspr.c line 556 at a77dfb9 · upstream ↗ 973 th = P_SpawnMobj (x,y,z, type); ⋮ 980 th->momx = FixedMul( th->info->speed, 981 finecosine[an>>ANGLETOFINESHIFT]);
linuxdoom-1.10/p_mobj.c lines 973–984 at a77dfb9 · upstream ↗ 1966 { // MT_ROCKET ⋮ 1982 20*FRACUNIT, // speed
linuxdoom-1.10/info.c lines 1966–1982 at a77dfb9 · upstream ↗ 1992 { // MT_PLASMA ⋮ 2008 25*FRACUNIT, // speed
linuxdoom-1.10/info.c lines 1992–2008 at a77dfb9 · upstream ↗ 2018 { // MT_BFG ⋮ 2034 25*FRACUNIT, // speed
linuxdoom-1.10/info.c lines 2018–2034 at a77dfb9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
- ^ abDoom's shotgun turns each of its 7 pellets by
(P_Random()-P_Random())<<18, up to about 5.6° either side, at one shared vertical slope; the pistol and chaingun turn their shots the same way except whileplayer->refireis 0, which covers the pistol's first shot and the chaingun's first two in a burst of held fire, and those fly straight.Codeid-Software/DOOMHow we know
627( mobj_t* mo, 628 boolean accurate ) ⋮ 636 if (!accurate) 637 angle += (P_Random()-P_Random())<<18; ⋮ 639 P_LineAttack (mo, angle, MISSILERANGE, bulletslope, damage);
linuxdoom-1.10/p_pspr.c lines 626–639 at a77dfb9 · upstream ↗ 684 P_BulletSlope (player->mo); ⋮ 686 for (i=0 ; i<7 ; i++) 687 P_GunShot (player->mo, false);
linuxdoom-1.10/p_pspr.c lines 684–687 at a77dfb9 · upstream ↗ 661 P_GunShot (player->mo, !player->refire);linuxdoom-1.10/p_pspr.c line 661 at a77dfb9 · upstream ↗ 753 P_GunShot (player->mo, !player->refire);linuxdoom-1.10/p_pspr.c line 753 at a77dfb9 · upstream ↗ 350 if ( (player->cmd.buttons & BT_ATTACK) ⋮ 354 player->refire++; 355 P_FireWeapon (player); ⋮ 359 player->refire = 0;
linuxdoom-1.10/p_pspr.c lines 350–359 at a77dfb9 · upstream ↗ 149 {SPR_PISG,0,4,{NULL},S_PISTOL2,0,0}, // S_PISTOL1 150 {SPR_PISG,1,6,{A_FirePistol},S_PISTOL3,0,0},// S_PISTOL2 151 {SPR_PISG,2,4,{NULL},S_PISTOL4,0,0}, // S_PISTOL3 152 {SPR_PISG,1,5,{A_ReFire},S_PISTOL,0,0}, // S_PISTOL4
linuxdoom-1.10/info.c lines 149–152 at a77dfb9 · upstream ↗ 188 {SPR_CHGG,0,4,{A_FireCGun},S_CHAIN2,0,0}, // S_CHAIN1 189 {SPR_CHGG,1,4,{A_FireCGun},S_CHAIN3,0,0}, // S_CHAIN2 190 {SPR_CHGG,1,0,{A_ReFire},S_CHAIN,0,0}, // S_CHAIN3
linuxdoom-1.10/info.c lines 188–190 at a77dfb9 · upstream ↗ 69#define ANG90 0x40000000linuxdoom-1.10/tables.h line 69 at a77dfb9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^ abA Wolfenstein 3D gun shot (
GunAttack) picks a target instead of tracing a bullet's path: it takes the nearest visible shootable actor withinshootdelta(a tenth of the view's width) of the screen's center column and hits it ifCheckLinereturns true, which its header comment describes as "a straight line between the player and ob is unobstructed"; as written, the loop's second pass looks only for actors nearer than that one, so a blocked line is a miss (computed). The damage is a roll ofUS_RndT(0 to 255) by the larger of the x and y tile distances: under 2 tiles the roll / 4 (0 to 63), under 4 the roll / 6 (0 to 42), and farther a miss when a first roll / 12 is below the distance, otherwise a second roll / 6.Codeid-Software/wolf3drevisedHow we know
1196 while (1) ⋮ 1198 oldclosest = closest; ⋮ 1200 for (check=ob->next ; check ; check=check->next) 1201 if ( (check->flags & FL_SHOOTABLE) 1202 && (check->flags & FL_VISABLE) 1203 && abs (check->viewx-centerx) < shootdelta ⋮ 1206 if (check->transx < viewdist) ⋮ 1208 viewdist = check->transx; 1209 closest = check; ⋮ 1213 if (closest == oldclosest) 1214 return; // no more targets, all missed ⋮ 1217 // trace a line from player to enemey ⋮
WOLFSRC/WL_AGENT.C lines 1196–1242 at 0516778 · upstream ↗ 1283 shootdelta = viewwidth/10;WOLFSRC/WL_MAIN.C line 1283 at 0516778 · upstream ↗ 1030= CheckLine ⋮ 1032= Returns true if a straight line between the player and ob is unobstructed
WOLFSRC/WL_STATE.C lines 1030–1032 at 0516778 · upstream ↗ 80; int US_RndT (void) 81; Return a random # between 0-255 ⋮ 94 mov al,[es:rndtable+BX] 95 xor ah,ah
WOLFSRC/ID_US_A.ASM lines 80–95 at 0516778 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Revised after a check by a second AI model, 2026-10-22.
Checked by a second AI model, 2026-10-25.
- ^ abAn enemy's shot in Wolfenstein 3D (
T_Shoot) traces no bullet either: when the shooter's area connects to the player's (areabyplayer) andCheckLinefinds a clear line, it hits if a roll ofUS_RndTis below a chance of 256 less 16 per tile of distance, or less 8 per tile when the shooter isn't visible on the player's screen (FL_VISABLE), starting from 160 instead of 256 when the player's thrust this frame (thrustspeed) reachedRUNSPEED(6,000); SS guards andbossobjbosses count two-thirds of the distance. A hit deals the roll >> 2 (0 to 63) under 2 tiles, >> 3 (0 to 31) under 4, and >> 4 (0 to 15) farther.Codeid-Software/wolf3dHow we know
3449 hitchance = 128; ⋮ 3451 if (!areabyplayer[ob->areanumber]) 3452 return; ⋮ 3454 if (!CheckLine (ob)) // player is behind a wall 3455 return; ⋮ 3457 dx = abs(ob->tilex - player->tilex); 3458 dy = abs(ob->tiley - player->tiley); 3459 dist = dx>dy ? dx:dy; ⋮ 3461 if (ob->obclass == ssobj || ob->obclass == bossobj) 3462 dist = dist*2/3; // ss are better shots ⋮ 3464 if (thrustspeed >= RUNSPEED) ⋮ 3466 if (ob->flags&FL_VISABLE) ⋮
WOLFSRC/WL_ACT2.C lines 3449–3490 at 0516778 · upstream ↗ 75#define RUNSPEED 6000WOLFSRC/WL_DEF.H line 75 at 0516778 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^Because the player's thrust scales with the frame's tics, a Wolfenstein 3D player who only walks straight forward with the keyboard (5,250 units a tic) counts as running for
T_Shoot's lower hit chance (thrustspeedat leastRUNSPEED, 6,000) on any frame that spans 2 or more tics, and only a 1-tic frame leaves that walk below it;thrustspeedsums everyThrustcall, so a walk that also strafes (5,250 + 5,250) passes it even in 1 tic (computed).Codeid-Software/wolf3drevisedHow we know
354 else ⋮ 356 if (Keyboard[dirscan[di_north]]) 357 controly -= BASEMOVE*tics;
WOLFSRC/WL_PLAY.C lines 354–357 at 0516778 · upstream ↗ 205 if (controly < 0) ⋮ 207 Thrust (ob->angle,-controly*MOVESCALE); // move forwards
WOLFSRC/WL_AGENT.C lines 205–207 at 0516778 · upstream ↗ 156 thrustspeed = 0;WOLFSRC/WL_AGENT.C line 156 at 0516778 · upstream ↗ 164 if (buttonstate[bt_strafe]) ⋮ 167 // strafing ⋮ 170 if (controlx > 0) ⋮ 172 angle = ob->angle - ANGLES/4; 173 if (angle < 0) 174 angle += ANGLES; 175 Thrust (angle,controlx*MOVESCALE); // move to left
WOLFSRC/WL_AGENT.C lines 164–175 at 0516778 · upstream ↗ 943 thrustspeed += speed;WOLFSRC/WL_AGENT.C line 943 at 0516778 · upstream ↗ 3464 if (thrustspeed >= RUNSPEED)WOLFSRC/WL_ACT2.C line 3464 at 0516778 · upstream ↗ 75#define RUNSPEED 6000WOLFSRC/WL_DEF.H line 75 at 0516778 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^Quake II's bullets, pellets, and railgun are hitscan:
fire_lead, whichfire_bullet(the machine gun and chaingun) andfire_shotgun(once per pellet, for both shotguns) call, traces a line 8192 units long in the frame the gun fires, bent by a random spread (crandom× the spread, each way) for each bullet or pellet, andfire_railtraces 8192 units and keeps tracing past every monster or player it hits, damaging each. The blaster, grenades, and rockets launch entities that travel: the player's blaster bolt at 1000 units/s, the launcher's grenade at 600, and the rocket at 650.Codeid-Software/Quake-2How we know
152 r = crandom()*hspread; 153 u = crandom()*vspread; 154 VectorMA (start, 8192, forward, end); 155 VectorMA (end, r, right, end); 156 VectorMA (end, u, up, end); ⋮ 165 tr = gi.trace (start, NULL, NULL, end, self, content_mask);
game/g_weapon.c lines 152–165 at 372afde · upstream ↗ 667 VectorMA (start, 8192, aimdir, end); ⋮ 672 while (ignore) ⋮ 674 tr = gi.trace (from, NULL, NULL, end, ignore, mask); ⋮ 681 else ⋮ 683 if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) 684 ignore = tr.ent; 685 else 686 ignore = NULL; ⋮ 688 if ((tr.ent != self) && (tr.ent->takedamage)) 689 T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN);
game/g_weapon.c lines 667–689 at 372afde · upstream ↗ 277void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) ⋮ 279 fire_lead (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod); ⋮ 287Shoots shotgun pellets. Used by shotgun and super shotgun. ⋮ 294 for (i = 0; i < count; i++) 295 fire_lead (self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod);
game/g_weapon.c lines 277–295 at 372afde · upstream ↗ 1014 fire_bullet (ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_MACHINEGUN);game/p_weapon.c line 1014 at 372afde · upstream ↗ 1151 fire_bullet (ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_CHAINGUN);game/p_weapon.c line 1151 at 372afde · upstream ↗ 1213 fire_shotgun (ent, start, forward, damage, kick, 500, 500, DEFAULT_DEATHMATCH_SHOTGUN_COUNT, MOD_SHOTGUN);game/p_weapon.c line 1213 at 372afde · upstream ↗ 1266 fire_shotgun (ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT/2, MOD_SSHOTGUN);game/p_weapon.c line 1266 at 372afde · upstream ↗ 728 fire_grenade (ent, start, forward, damage, 600, 2.5, radius);game/p_weapon.c line 728 at 372afde · upstream ↗ 620void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) ⋮ 628 VectorScale (dir, speed, rocket->velocity); 629 rocket->movetype = MOVETYPE_FLYMISSILE;
game/g_weapon.c lines 620–629 at 372afde · upstream ↗ 783 fire_rocket (ent, start, forward, damage, 650, damage_radius, radius_damage);game/p_weapon.c line 783 at 372afde · upstream ↗ 832 fire_blaster (ent, start, forward, damage, 1000, effect, hyper);game/p_weapon.c line 832 at 372afde · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
Sources
Code
- Software, id. 2024. “DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10).” Commit a77dfb9. id-Software/DOOM @ a77dfb9 · licence, all uses
- Software, id. 2012. “Wolfenstein 3D: the source release (id-Software/wolf3d).” Commit 0516778. id-Software/wolf3d @ 0516778 · licence, all uses