Quick answer: Box2D steps physics at a fixed rate (60 Hz by default in Construct 3). A bullet moving faster than its own collider width per step can teleport across a thin wall between samples — the engine never sees the overlap. Enable the Bullet flag on fast objects to turn on continuous collision detection, increase velocity iterations, thicken your walls, and add a raycast as a safety net.

You build a top-down shooter in Construct 3 with the Physics behavior on your bullets so they ricochet realistically off walls. The slow rounds work great. The high-velocity rifle? Half the shots vanish into the geometry. They fly straight through 16-pixel walls and hit nothing on the other side, or worse, they hit the player who is standing behind the wall. The Box2D physics engine inside Construct 3 cannot see collisions that happen between simulation steps — and a fast bullet is moving faster than the step rate can keep up with. The fix is a combination of one flag, a few project setting bumps, and a raycast safety net for the truly fast cases.

The Symptom

Fast-moving Physics objects fail to register collisions with thin static obstacles:

Bullets pass through walls. A projectile with high velocity continues on its trajectory through what should be a solid barrier. The wall has a Solid behavior or Physics with Immovable, but no collision triggers fire.

Inconsistent at different speeds. The same projectile collides correctly at low speeds but tunnels at high speeds. There is a clear threshold — usually right around when the per-step distance exceeds the collider width.

Frame-rate dependent. The bug appears more often on high-refresh displays because the game runs faster relative to the physics step, or appears more often on low-spec hardware where physics steps are skipped to keep up.

Walls miss too. The reverse case: a fast wall slamming down on a slow object passes through it. Tunneling does not care which body is moving, only that the relative velocity per step is too high.

What Causes This

Discrete time steps. Box2D advances the simulation in fixed steps. At each step, every body has a position; between steps, it has a velocity but no checked path. Collision detection runs at the step boundaries: the engine asks “at this instant, does anything overlap?” If your bullet is at x = 50 on step N and at x = 200 on step N+1, and the wall is at x = 100 with a width of 16 pixels, the bullet was never on top of the wall at any sampled instant. No collision fires.

Default step rate. Construct 3’s Physics behavior runs Box2D at 60 Hz regardless of your game’s frame rate. At 1000 px/s velocity, an object travels about 16.7 pixels per step. Any wall thinner than that has a chance of being skipped over — and the chance grows with velocity. At 3000 px/s the per-step travel is 50 pixels, larger than most level geometry.

Bullet flag not set. Box2D has a built-in solution called continuous collision detection (CCD) that sweeps the body’s collider along its movement path and detects any obstacle it would cross. CCD is opt-in because it is more expensive than discrete checks. In Construct 3 you turn it on per-object via the Set bullet action. Without it, no amount of iteration tuning will catch the truly fast cases.

Density mismatch. A very low-density bullet hitting a very high-density wall produces a tiny resolution impulse. Even when the engine sees the collision, the bullet may pass through because the calculated penetration is small enough to fall under the slop tolerance. Setting reasonable matching densities helps the resolution math behave.

The Fix

Step 1: Enable the Bullet flag. On every projectile that needs reliable collision, call Physics: Set bullet with the value true at creation. This is the single most effective fix. With CCD enabled, Box2D will detect the wall even if the projectile would otherwise tunnel through it.

// In an On Created event for the Projectile object:
// Action: Projectile -> Physics: Set bullet -> True

// Equivalent in a script (advanced users):
const body = projectile.behaviors.Physics;
body.setBullet(true);

// Apply the initial velocity AFTER setting bullet
body.setLinearVelocity(
  Math.cos(angleRad) * speed,
  Math.sin(angleRad) * speed
);

Set the bullet flag before you apply the velocity. If you apply velocity first, the first step happens with CCD off and may already tunnel before the flag takes effect. Always set body properties in the order: density → restitution → bullet → velocity.

Step 2: Bump iteration counts. In Project Properties, find the Physics section and raise Velocity iterations from the default 8 to 12 or 16, and Position iterations from 3 to 4 or 5. More iterations give Box2D more passes to converge on a correct collision response, which helps the bullet behave correctly even at high relative velocity. The cost is roughly linear in iteration count, but unless you have hundreds of physics bodies it is unnoticeable.

Step 3: Add a raycast safety net. The Bullet flag plus higher iterations will solve maybe 95% of cases. For the last 5% — truly extreme velocities, very thin walls, complex collision shapes — add a raycast on every tick from the projectile’s previous position to its current position. If the ray hits a solid, snap the projectile back to the intersection point and trigger your hit logic manually. This is the belt-and-suspenders approach used by most professional engines for bullets.

// In a Projectile script behavior or an event tick:
const prevX = projectile.instVars.lastX;
const prevY = projectile.instVars.lastY;
const curX = projectile.x;
const curY = projectile.y;

// Cast a ray from previous to current position
const hit = runtime.objects.Wall.testRayCast(
  prevX, prevY, curX, curY
);

if (hit) {
  // Snap to the impact point and trigger collision
  projectile.x = hit.x;
  projectile.y = hit.y;
  projectile.dispatchEvent(new Event("hitwall"));
}

// Update last position for next tick
projectile.instVars.lastX = projectile.x;
projectile.instVars.lastY = projectile.y;

The raycast catches anything physics misses. If you do not want to write a script, the same logic is achievable in events using a family of Wall objects and the Pick by collision with line conditions, though it is more verbose.

Thicken Your Walls

If your level walls are 4 pixels thick because that looks great in the art, give them a 32-pixel collision polygon that extends behind the visible wall. The visible wall stays thin; the physics collider is fat. Edit the collision polygon in the image editor — players never see the polygon, only the texture. This is a free fix that does not cost any CPU and works even without the Bullet flag for moderately fast objects.

The general rule of thumb: every collider that interacts with high-velocity objects should be at least as thick as the maximum per-step distance of those objects. At 60 Hz step rate and 1500 px/s max velocity, that is 25 pixels minimum thickness. At 2500 px/s, 42 pixels. Round up generously.

When to Lower the Step Rate

For games where every projectile is a bullet and you cannot tolerate even rare tunneling, you can globally lower the simulation timestep so Box2D runs more often per second. In Construct 3 there is no first-class setting for this, but you can use the Physics action Step world to advance the simulation manually multiple times per game tick. Two extra steps per tick (effectively 180 Hz physics) catches almost all tunneling at the cost of 3× physics CPU. Use only if needed and only on the affected projectiles.

“Tunneling is not a bug, it is the price of fixed-step physics. The Bullet flag exists for exactly this reason — if you have a fast object, set it. If you have a really fast object, raycast it too.”

Related Issues

If your physics objects fall through static floors at startup, see Construct 3 Physics Objects Falling Through Floor. If non-Physics behaviors collide reliably but Physics ones do not, the issue is almost certainly a missing Bullet flag rather than a Construct 3 bug.

Bullet flag + 16 velocity iterations + a raycast safety net — that combo eliminates tunneling for good.