| name | unity-physicscore2d-queries |
| description | Patterns for single-shot Unity PhysicsCore2D queries — raycasts (sight lines, ground checks, bullet hit-tests), overlap tests (trigger zones, AOE damage, ground checks), shape casts (projectile sweeps), distance/closest-point queries (AI awareness), and time-of-impact tests. Covers QueryFilter setup, layer masking, sorting hits, and the geometry-vs-world variants. For bulk/batched queries see unity-physicscore2d-batching. For full member API see unity-physicscore2d-queries-api. |
Unity PhysicsCore2D — Query Patterns
Most physics gameplay logic is built from a handful of query primitives: cast a ray, overlap a shape, find the closest point. PhysicsCore2D exposes these on PhysicsWorld (world queries) and on individual *Geometry types (shape-vs-shape tests).
For the full type/method API surface (every overload, signature, and XML doc), see unity-physicscore2d-queries-api. For batched / job-based queries see unity-physicscore2d-batching. This skill focuses on single-shot patterns.
Quick decision rules
| Need | Tool |
|---|
| "What's directly between A and B?" | world.CastRay |
| "Is anything inside this circle/box right now?" | world.OverlapShape (or OverlapAABB for axis-aligned boxes) |
| "Will this projectile hit anything if it moves like this?" | world.CastShape (sweep) |
| "How far is the nearest enemy?" | world.OverlapShape with a search radius + iterate |
| "Will these two specific shapes collide if A moves?" | Geometry-level CastShape on the input geometry |
| "Just give me a yes/no, no contact info" | world.TestOverlap* (cheaper than full overlap) |
| "Bulk-query 1000 rays in parallel" | See unity-physicscore2d-batching |
QueryFilter — always set one explicitly
Almost every query takes a PhysicsQuery.QueryFilter. Defaults match everything; you almost always want to narrow it:
using Unity.U2D.Physics;
var everything = PhysicsQuery.QueryFilter.Everything;
var def = PhysicsQuery.QueryFilter.defaultFilter;
var moverFilter = new PhysicsQuery.QueryFilter
{
categories = new PhysicsMask(1),
hitCategories = new PhysicsMask(0) | new PhysicsMask(2)
};
var customFilter = new PhysicsQuery.QueryFilter(
categories: PhysicsMask.All,
hitCategories: new PhysicsMask(3),
ignoreFilter: default);
Source: PhysicsQueryJob.cs (PhysicsQuery.QueryFilter.Everything), CharacterMover.cs (categories/hitCategories pattern), queries-api/SKILL.md (field list).
Cost: filtering happens early in the broadphase, so a tight filter is dramatically cheaper than processing every hit and discarding most.
Pattern 1 — Sight line (raycast)
"Can A see B without walls in the way?"
using Unity.U2D.Physics;
var rayInput = new PhysicsQuery.CastRayInput(origin: eyePosition, translation: targetPosition - eyePosition);
var rayInput2 = PhysicsQuery.CastRayInput.FromTo(eyePosition, targetPosition);
using var results = world.CastRay(
rayInput,
PhysicsQuery.QueryFilter.Everything,
PhysicsQuery.WorldCastMode.Closest);
bool canSee = results.Length == 0;
if (!canSee)
{
var hit = results[0];
Vector2 hitPoint = hit.point;
Vector2 hitNormal = hit.normal;
float fraction = hit.fraction;
var hitShape = hit.shape;
}
Source: CastRayQuery.cs (exact pattern), Slicing.cs (CastRayInput struct literal + QueryFilter with hitCategories), world-api/SKILL.md (CastRay signature), queries-api/SKILL.md (WorldCastResult fields, CastRayInput fields).
hit.fraction is in 0..1 along the cast (0 = origin, 1 = full distance). hit.point is the world-space hit; hit.normal is the surface normal.
Pattern 2 — Ground check (overlap)
A small overlap test at the character's feet is more reliable than a single raycast (which can fall through gaps between ground shapes):
using Unity.U2D.Physics;
var feetCircle = new CircleGeometry
{
center = (Vector2)transform.position + Vector2.down * 0.55f,
radius = 0.25f
};
var groundFilter = new PhysicsQuery.QueryFilter
{
categories = PhysicsMask.All,
hitCategories = new PhysicsMask(0)
};
bool isGrounded = world.TestOverlapGeometry(feetCircle, groundFilter);
using var hits = world.OverlapGeometry(feetCircle, groundFilter, Unity.Collections.Allocator.Temp);
if (hits.Length > 0)
{
var groundShape = hits[0].shape;
}
Source: PhysicsQueryJob.cs (TestOverlapPoint pattern — same boolean-test idiom), CharacterMover.cs (categories/hitCategories), world-api/SKILL.md (TestOverlapGeometry(CircleGeometry, QueryFilter) and OverlapGeometry(CircleGeometry, QueryFilter, Allocator) signatures).
TestOverlapShape returns a bool — much cheaper than OverlapShape if you don't need the hit list.
Pattern 3 — AOE damage (overlap, iterate)
"Apply damage to everything in this circle."
using Unity.U2D.Physics;
var blastCircle = new CircleGeometry
{
center = blastPosition,
radius = blastRadius
};
var aoeFilter = new PhysicsQuery.QueryFilter
{
categories = PhysicsMask.All,
hitCategories = PhysicsMask.All
};
using var hits = world.OverlapGeometry(blastCircle, aoeFilter, Unity.Collections.Allocator.Temp);
for (int i = 0; i < hits.Length; i++)
{
var result = hits[i];
if (!result.isValid)
continue;
var hitBody = result.shape.body;
if (!hitBody.isValid)
continue;
float dist = Vector2.Distance(blastPosition, hitBody.position);
float damage = maxDamage * (1f - dist / blastRadius);
ApplyDamage(hitBody, damage);
}
Source: TestShadowRegion.cs (world.OverlapGeometry(polygonGeometry, QueryFilter, allocator) — exact call pattern), SpriteDestruction.cs (OverlapPoint with category filter — same idiom for geometry), world-api/SKILL.md (OverlapGeometry(CircleGeometry, QueryFilter, Allocator) signature), queries-api/SKILL.md (WorldOverlapResult.shape, WorldOverlapResult.isValid).
Always Dispose the results array (or use using). Allocator.Temp is fastest for per-frame queries.
Pattern 4 — Projectile sweep (shape cast)
For fast-moving projectiles, a single raycast misses thin geometry (tunneling). Cast the actual projectile shape over its movement:
using Unity.U2D.Physics;
var bulletCircle = new CircleGeometry
{
center = projectilePosition,
radius = bulletRadius
};
var travelVector = projectileVelocity * Time.fixedDeltaTime;
var projectileFilter = new PhysicsQuery.QueryFilter
{
categories = new PhysicsMask(2),
hitCategories = new PhysicsMask(0) | new PhysicsMask(1)
};
using var hits = world.CastGeometry(
bulletCircle,
travelVector,
projectileFilter,
PhysicsQuery.WorldCastMode.Closest);
if (hits.Length > 0)
{
var hit = hits[0];
Vector2 impactPoint = hit.point;
Vector2 impactNormal = hit.normal;
float travelFrac = hit.fraction;
var hitShape = hit.shape;
SpawnImpactVFX(impactPoint, impactNormal);
hitShape.body.ApplyLinearImpulseToCenter(travelVector.normalized * impactForce);
}
Source: CastGeometryQuery.cs (exact world.CastGeometry(geometry, castTranslation, QueryFilter.Everything) pattern), CharacterMover.cs (CastShapeProxy with category filter — same sweep concept), world-api/SKILL.md (CastGeometry(CircleGeometry, Vector2, QueryFilter, WorldCastMode, Allocator) signature), queries-api/SKILL.md (WorldCastResult fields).
For very high-speed bodies, prefer enabling continuous collision (continuousAllowed on the world plus continuous = true on the body) over manual sweeps — but for one-shot logic (raycasts, hitscan), shape casts are simpler and don't require dedicated bodies.
Pattern 5 — Closest-point query (AI awareness)
"Where is the nearest wall surface to me?"
using Unity.U2D.Physics;
var distInput = new PhysicsQuery.DistanceInput
{
shapeProxyA = agentShape.CreateShapeProxy(),
transformA = agentShape.body.transform,
shapeProxyB = obstacleShape.CreateShapeProxy(),
transformB = obstacleShape.body.transform,
useRadii = true
};
PhysicsQuery.DistanceResult dist = PhysicsQuery.ShapeDistance(distInput);
if (dist.distance < alertRange)
{
Vector2 nearestOnAgent = dist.pointA;
Vector2 nearestOnObstacle = dist.pointB;
Vector2 separationAxis = dist.normal;
}
Source: PhysicsAPIExtensions.cs (exact PhysicsQuery.ShapeDistance(new PhysicsQuery.DistanceInput { shapeProxyA, shapeProxyB, transformA, transformB, useRadii }) pattern — real project code), queries-api/SKILL.md (DistanceInput fields, DistanceResult fields, ShapeDistance signature).
PhysicsQuery.SegmentDistanceResult gives both endpoints' nearest pair on a single line segment, useful for cone-of-vision and beam attacks.
Pattern 6 — Will these two collide if I move? (geometry-level cast)
When you don't want to disturb the world but need to test "if my character moved by delta, would my proposed shape hit this specific obstacle?", use the geometry-level CastShape directly:
using Unity.U2D.Physics;
var capsuleGeometry = new CapsuleGeometry
{
center1 = characterFeet,
center2 = characterHead,
radius = 0.4f
};
using var hits = world.CastGeometry(
capsuleGeometry,
desiredMove,
new PhysicsQuery.QueryFilter
{
categories = PhysicsMask.All,
hitCategories = new PhysicsMask(0)
},
PhysicsQuery.WorldCastMode.Closest);
if (hits.Length > 0)
{
var hit = hits[0];
Vector2 blocked = hit.normal * Vector2.Dot(desiredMove, hit.normal);
Vector2 slideDir = desiredMove - blocked;
desiredMove = slideDir;
}
Source: CastGeometryQuery.cs (world.CastGeometry(geometry, translation, QueryFilter.Everything) — direct match), world-api/SKILL.md (CastGeometry(CapsuleGeometry, Vector2, QueryFilter, WorldCastMode, Allocator) overload), queries-api/SKILL.md (WorldCastResult.normal, WorldCastResult.fraction).
Useful for character controllers that want to test their next step without first creating a shape.
Pattern 7 — Time of impact (precise CCD)
For deterministic "when exactly do these two moving bodies touch?" use PhysicsQuery.TimeOfImpactInput:
using Unity.U2D.Physics;
var bulletProxy = new PhysicsShape.ShapeProxy(new CircleGeometry { center = Vector2.zero, radius = bulletRadius });
var targetProxy = new PhysicsShape.ShapeProxy(new CircleGeometry { center = Vector2.zero, radius = targetRadius });
var bulletSweep = new PhysicsQuery.ShapeSweep
{
localCOM = Vector2.zero,
positionStart = bulletStartPos,
positionEnd = bulletStartPos + bulletVelocity * deltaTime,
rotationStart = PhysicsRotate.identity,
rotationEnd = PhysicsRotate.identity
};
var targetSweep = new PhysicsQuery.ShapeSweep
{
localCOM = Vector2.zero,
positionStart = targetStartPos,
positionEnd = targetStartPos + targetVelocity * deltaTime,
rotationStart = PhysicsRotate.identity,
rotationEnd = PhysicsRotate.identity
};
var toiResult = PhysicsQuery.ShapeTimeOfImpact(new PhysicsQuery.TimeOfImpactInput
{
shapeProxyA = bulletProxy,
shapeSweepA = bulletSweep,
shapeProxyB = targetProxy,
shapeSweepB = targetSweep,
maxFraction = 1f
});
if (toiResult.impactState == PhysicsQuery.TimeOfImpactResult.State.Hit)
{
float hitFraction = toiResult.fraction;
Vector2 hitPoint = toiResult.point;
Vector2 hitNormal = toiResult.normal;
Vector2 impactPos = Vector2.Lerp(bulletStartPos, bulletStartPos + bulletVelocity * deltaTime, hitFraction);
}
Source: queries-api/SKILL.md (TimeOfImpactInput fields: shapeProxyA, shapeSweepA, shapeProxyB, shapeSweepB, maxFraction; TimeOfImpactResult fields: fraction, impactState, point, normal; State.Hit enum value; ShapeTimeOfImpact static method signature). No real project file uses this method — API-only fallback.
Reach for this for replay systems, deterministic networking, or precise hit-prediction. Overkill for casual gameplay queries.
QueryFilter — common recipes
using Unity.U2D.Physics;
var hitAll = PhysicsQuery.QueryFilter.Everything;
var sightFilter = new PhysicsQuery.QueryFilter
{
categories = PhysicsMask.All,
hitCategories = new PhysicsMask(0)
};
const int envLayer = 0;
const int enemyLayer = 1;
const int projectileLayer = 2;
var bulletFilter = new PhysicsQuery.QueryFilter
{
categories = new PhysicsMask(projectileLayer),
hitCategories = new PhysicsMask(envLayer) | new PhysicsMask(enemyLayer)
};
var destructibleMask = new PhysicsMask(3);
var debrisMask = new PhysicsMask(4);
var aoeFilter = new PhysicsQuery.QueryFilter
{
categories = PhysicsMask.All,
hitCategories = destructibleMask | debrisMask
};
var ignoreFilter = world.ignoreFilter;
var filteredQuery = new PhysicsQuery.QueryFilter
{
categories = PhysicsMask.All,
hitCategories = PhysicsMask.All,
ignoreFilter = ignoreFilter
};
Source: PhysicsQueryJob.cs (QueryFilter.Everything), CharacterMover.cs (categories + hitCategories pattern with PhysicsMask constants), Slicing.cs (new PhysicsQuery.QueryFilter { categories = PhysicsMask.All, hitCategories = m_DestructibleMask | m_GroundMask }), SpriteDestruction.cs (category/hitCategory filter on OverlapPoint), queries-api/SKILL.md (QueryFilter field list).
Sorting and limiting hits
OverlapShape returns hits in broadphase order, not sorted by distance. If you need the closest:
using Unity.U2D.Physics;
using Unity.Collections;
using var sortedHits = world.CastRay(
PhysicsQuery.CastRayInput.FromTo(origin, target),
PhysicsQuery.QueryFilter.Everything,
PhysicsQuery.WorldCastMode.AllSorted);
var searchCircle = new CircleGeometry { center = searchOrigin, radius = searchRadius };
using var overlapHits = world.OverlapGeometry(searchCircle, PhysicsQuery.QueryFilter.Everything, Allocator.Temp);
PhysicsShape closestShape = default;
float closestDistSq = float.MaxValue;
for (int i = 0; i < overlapHits.Length; i++)
{
var result = overlapHits[i];
if (!result.isValid) continue;
float distSq = ((Vector2)result.shape.body.position - searchOrigin).sqrMagnitude;
if (distSq < closestDistSq)
{
closestDistSq = distSq;
closestShape = result.shape;
}
}
using var firstHit = world.CastRay(
new PhysicsQuery.CastRayInput(origin, direction * maxRange),
PhysicsQuery.QueryFilter.Everything,
PhysicsQuery.WorldCastMode.Closest);
bool blocked = firstHit.Length > 0;
Source: Slicing.cs (WorldCastMode.AllSorted — exact usage in a reflection ray loop), CastRayQuery.cs + PhysicsQueryJob.cs (WorldCastMode.Closest), world-api/SKILL.md (OverlapGeometry(CircleGeometry, QueryFilter, Allocator) — 3-arg signature confirmed), queries-api/SKILL.md (WorldCastMode enum values).
For "first hit only", CastRay already returns the nearest along the ray.
Best practices
- Always set a
QueryFilter — defaults match everything and are slow.
- Prefer
TestOverlap* for boolean checks — half the work of a full Overlap*.
- Use
Allocator.Temp for per-frame query results — fastest, auto-freed within the frame.
- Sweep, don't single-raycast, fast objects — single rays miss between fixed-step samples.
- Cache shape geometry — reuse
CircleGeometry/CapsuleGeometry value instances rather than allocating per call.
- Run queries during simulation OR between simulations, not both — see WORM rules in
unity-physicscore2d umbrella for thread-safety details.
Common pitfalls
- Casting a ray of length
0 — returns empty even if the origin overlaps. Use OverlapPoint for "is this point inside anything?".
- Forgetting
hitCategories — QueryFilter.Everything hits triggers and sensors too; narrow hitCategories to exclude them for solid-geometry-only queries.
- Iterating an undisposed
OverlapShape result — leaks Temp memory. Use using.
- Treating
hit.fraction == 1 as "no hit" — the cast hit at the very end of the translation. 0..1 are all valid hits.
- Querying inside a contact callback while the world is locked — see WORM model.