Find the nearest solid body along a line without moving it.
dotnet new console dotnet add package RecursiveAI.OneGame.Physics
Requires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.
Fixed and FVec2 are onegame’s deterministic fixed-point number and 2D vector types.
using OneGame.Core; using OneGame.Physics; var space = new PhysicsSpace2D(); // size, center space.AddBox(new FVec2(1, 2), new FVec2(3, 0)); if (space.Raycast(FVec2.Zero, new FVec2(1, 0), 10, out var hit)) Console.WriteLine(hit.Distance);
Casts right from the origin and prints 2.5.
2.5Unzip the example, then dotnet run.
The full program
The full program identifies the static wall, prints the hit point, surface normal and distance, then casts upward to show a miss.
using OneGame.Core; using OneGame.Physics; var space = new PhysicsSpace2D(); // size, center var wall = space.AddBox( new FVec2(1, 2), new FVec2(3, 0), new BodyOptions { Kind = BodyKind.Static }); if (space.Raycast(FVec2.Zero, new FVec2(1, 0), 10, out var hit)) { Console.WriteLine($"Hit wall: {hit.Body == wall}"); Console.WriteLine($"Point: {hit.Point}"); Console.WriteLine($"Normal: {hit.Normal}"); Console.WriteLine($"Distance: {hit.Distance}"); } var upwardHit = space.Raycast( FVec2.Zero, new FVec2(0, 1), 10, out _); Console.WriteLine($"Upward hit: {upwardHit}");
dotnet run prints:
Hit wall: True Point: (2.5, 0) Normal: (-1, 0) Distance: 2.5 Upward hit: False
A zero direction or a nonpositive maximum distance throws ArgumentOutOfRangeException.
Variations
Filter the ray by layer
using OneGame.Core; using OneGame.Physics; var space = new PhysicsSpace2D(); space.AddBox(new FVec2(1, 1), new FVec2(3, 0), new BodyOptions { Layer = 4 }); Console.WriteLine(space.Raycast(FVec2.Zero, new FVec2(1, 0), 10, out _, layers: 1u << 4));
TrueRelated recipes
- 2D platformer character controller in C#
- Filter 2D physics collision layers in C#
- Apply 2D forces and impulses in C#
The short answer
Use PhysicsSpace2D.Raycast(origin, direction, maxDistance, out hit). The return value tells you whether a solid body was found. On a hit, RayHit contains the body reference, world-space point, surface normal and distance from the origin. On a miss, the physics library sets the hit record to its default value, so branch on the Boolean before reading it.
The raycast method finds the nearest body and skips triggers. It takes Fixed and FVec2, which lets simulation code query the same physics state that is stepped, saved and hashed with the game world.
How do you use a raycast in 2D?
Choose the starting position, a nonzero direction and a positive reach. The direction can have any nonzero length: reach is supplied separately through maxDistance. The top snippet adds a box before querying it, so there is a real object to hit even though no simulation step has run yet.
The wall in the full program is centered at x = 3 and has width 1, so the rightward ray first reaches its left face at x = 2.5. That explains the printed distance and point. The normal points back toward the ray’s origin side, which in this case is left. Use the returned body reference to identify the thing hit rather than treating a nonzero distance as an identity check.
A ray is not a moving collider. Running a query does not apply an impulse, move a body or make a gameplay decision for you. If a hit should cause damage or activate an object, make that decision in simulation code and keep any resulting state on the appropriate entity. When querying in OnTick, remember the physics system runs after entity ticks: your query sees the state available before that upcoming step.
Can raycasts hit triggers?
This 2D physics API skips triggers. Its public ray method has a layer-mask parameter, but no parameter that enables trigger hits. Setting a trigger’s layer bit in the mask does not override that behavior. For things entering and leaving a zone, use the space’s trigger events instead of expecting this ray query to report the zone.
The physics library’s normal is documented as zero if the ray begins inside a body. Do not normalize that result blindly or assume every hit normal is an outward direction suitable for a reflection. An inside-origin hit and an ordinary surface entry need different treatment if your game uses the normal to decide a response.
How do you make a raycast ignore a layer?
Build the layers argument as a bit mask. The variation selects layer 4 with 1u << 4, not the integer 4. Combine several allowed layer bits with OR, or leave unwanted bits unset. The default mask includes all layers. Query filtering is separate from the matrix that determines whether pairs of bodies collide.
If a ray misses unexpectedly, check the origin and direction in physics coordinates first. Simulation Y points up; screen pixels may point down. Then check reach, body layer and trigger status. The debug overlay recipe shows how to draw the actual collider outlines using a consistent world-to-screen transform.