Spatial queries
Contact events tell you when something touched you. Spatial queries let you ask on your own initiative.
let hit = spatial::raycast(origin, direction, max_distance).await; // closest hit, or nonelet near = spatial::overlap(center, radius).await; // your entities in a sphereBoth are read-only: they resolve against the physical world without touching the simulation,
so they are cheap enough to use from update.
What a ray can see
Section titled “What a ray can see”- map geometry — the world’s own colliders
- collision proxies of archetypes flagged
interactorcontact
and nothing else. In particular:
Hits report geometry; handles stay yours
Section titled “Hits report geometry; handles stay yours”if let Some(hit) = spatial::raycast(eye, forward, 50.0).await { // hit.position, hit.normal, hit.distance always if let Some(entity) = hit.entity { // ...only when it was one of YOUR identified entities }}A hit on another mod’s entity reports the position, normal and distance but no handle. It
occludes; that is all you learn. Same rule as find: a handle is permission to mutate, so
handles never cross mods.
overlap follows the same scoping — it returns your own identified entities inside the
sphere, capped at 1024 results like find.
Line of sight, concretely
Section titled “Line of sight, concretely”async fn can_see(eye: Vec3, target: Vec3) -> bool { let to = Vec3 { x: target.x - eye.x, y: target.y - eye.y, z: target.z - eye.z }; let dist = (to.x * to.x + to.y * to.y + to.z * to.z).sqrt(); match spatial::raycast(eye, to, dist).await { // something solid is between us Some(hit) => hit.distance >= dist - 0.1, // clear line None => true, }}The watchman reference mod is built on exactly this, and it also shows the trap: a thin ray
can thread a gap and land on a roof, reporting a “clear” line to somewhere the player is not.
Sanity-check what you hit — compare heights, cast more than one ray — rather than trusting a
single result.
Timing
Section titled “Timing”Colliders do not exist until the map has finished loading, so a query during init finds
nothing. Cast from update and retry; a first successful ground probe is itself a decent
signal that the world is ready.