Cast a shape
Goal
Check a straight throw path with a sphere, check a character teleport with a capsule, and find the bodies on a pressure plate with a box overlap.
Files you will edit
src/game.ts
Steps
In the third-person template's
src/game.ts, addphysicsandTransformto the SDK import andHERO_RADIUS/HERO_HALF_HEIGHTto the./prefabsimport. Add these reusable query records abovedefineGame.tsconst queryRotation = { x: 0, y: 0, z: 0, w: 1 }; const throwOrigin = { x: 0, y: 0, z: 0 }; const throwDirection = { x: 0, y: 0, z: -1 }; const teleportStart = { x: -2, y: 1.3, z: -3 }; const teleportDirection = { x: 1, y: 0, z: 0 }; const teleportSpot = { x: 0, y: 1.3, z: -3 }; const plateCentre = { x: 0, y: 0.2, z: -5 }; const plateHalf = { x: 1, y: 0.15, z: 1 }; const queryLayers = { defaultLayer: true, staticGeometry: true, player: true }; const plateLayers = { defaultLayer: true, player: true }; let throwClear = false; let plateCount = 0;Add this system above
defineGameand insertshapeChecksafterlocomotionSystemin the existingsystemsarray. Press G to test a 6 m throw segment toward -Z from the hero's eye; the 15 cm sphere catches obstacles a thin ray would miss. Consume its hit before running another query.tsfunction shapeChecks(ctx: GameContext): void { if (ctx.player === 0) return; if (ctx.input.pressed('G')) { throwOrigin.x = Transform.x[ctx.player]; throwOrigin.y = Transform.y[ctx.player] + 0.5; throwOrigin.z = Transform.z[ctx.player]; const hit = physics.sphereCast( throwOrigin, 0.15, throwDirection, 6, queryLayers, ctx.player, ); throwClear = hit === null; } if (ctx.input.pressed('T')) { const blocked = physics.capsuleCast( teleportStart, HERO_RADIUS, HERO_HALF_HEIGHT, queryRotation, teleportDirection, 2, queryLayers, ctx.player, ); if (blocked === null) { const occupied = physics.overlapCapsule( teleportSpot, HERO_RADIUS, HERO_HALF_HEIGHT, queryRotation, 1, queryLayers, ctx.player, ); if (occupied.length === 0) { physics.teleport(ctx.player, teleportSpot.x, teleportSpot.y, teleportSpot.z); } } } const onPlate = physics.overlapBox(plateCentre, plateHalf, queryRotation, 16, plateLayers); plateCount = onPlate.length; }T checks a 2 m arrival corridor from
teleportStarttoteleportSpot, then overlaps the destination before teleporting. Both centres must be above the floor by at least the capsule's radius plus half-height; neither test includes the player's current body. Use an overlap for an occupied spot: a sweep alone is not a reliable initial-penetration check. These queries describe a straight segment; a ballistic throw needs several segments.Reset
throwClear = falseandplateCount = 0in the existinginit. Use those values in your game rules or HUD:throwClearis permission to launch, andplateCount > 0means the plate is occupied. An overlap returns entity ids in its hits if you need to distinguish the hero from a crate. The plate filter skipsstaticGeometry, so the floor does not count as an occupant; its box also starts 5 cm above the floor. The 16-result cap is intentional; raise it for a crowded plate. Queries ignoresolidOnly, including sensors on selected layers just like raycast.
Verify
sh
npm run devStand with clear space toward -Z and press G: throwClear becomes true. Stand near a wall edge where a line fits but a 30 cm wide projectile does not: it becomes false. Press T: the hero arrives at (0, 1.3, -3) only if the corridor and destination are clear. Walk across (0, 0, -5): plateCount becomes positive, then returns to zero when the volume is empty. Adjust the three test locations if your arena has scenery there.