"Trace-based kinematic controllers don't fire triggers"
Pickups, zones, or any trigger volume that relies on OnTriggerEnter / ITriggerListener silently never fires when the player walks through it — even though the volumes work fine with physics-driven bodies.
A trace-based kinematic controller (one that moves by sweeping traces each tick rather than using the built-in CharacterController physics body) has no collider component of its own. Collision lives entirely inside the sliding mover's traces. Because there is no Collider on the player's GameObject, the physics engine never detects an overlap with trigger volumes, and trigger callbacks never fire.
Replace trigger-volume detection with distance polling on the fixed tick:
protected override void OnFixedUpdate()
{
var player = Scene.GetAllComponents<MyPlayer>().FirstOrDefault();
if ( player is null ) return;
float dist = player.WorldPosition.Distance( WorldPosition );
if ( dist < radius * 39.37f ) // radius in metres → engine units
{
Consume();
}
}Key points:
- Poll against the player singleton each tick — no collider, no trigger, just a distance check.
- Convert your design radius from metres to engine units (
× 39.37) at the comparison boundary. - For area zones (rectangular regions, not spheres), use axis-aligned bounds checks or a 2D point-in-polygon test instead of distance.
- This pattern is cheap — one distance comparison per pickup per tick is negligible.
The trace mover is invisible to the physics broadphase because it never registers a body. Distance-polling replaces the broadphase overlap test with an explicit per-tick check. It's the same final test the engine would run internally, just done in game code.