Skip to main content

Tracing

Namespace: DeadworksManaged.Api

Cast rays and shapes through the world using the VPhys2 physics system. Useful for hit detection, line of sight, and collision checks.

Trace (Static Class)

All methods are no-ops if the physics query system is not yet ready.

Simple Ray Cast

Fire a line ray from start to end:

var result = Trace.Ray(
start: pawn.EyePosition,
end: pawn.EyePosition + forwardDirection * 1000f,
mask: MaskTrace.Solid,
ignore: pawn // skip the casting entity
);

if (result.DidHit)
{
Console.WriteLine($"Hit at {result.HitPosition}, fraction: {result.Fraction}");
}

TraceShape (Advanced)

Full control with custom ray shape and filter:

var ray = new Ray_t();
// Initialize ray shape...

var filter = new CTraceFilter();
// Configure filter...

CGameTrace trace;
Trace.TraceShape(start, end, ray, filter, out trace);

if (trace.DidHit)
{
var hitEntity = trace.HitEntity;
Console.WriteLine($"Hit: {hitEntity?.Classname} at {trace.HitPoint}");
}

SimpleTrace

Convenience method that builds the filter and ray from parameters:

CGameTrace trace;
Trace.SimpleTrace(
start, end,
RayType_t.Line,
RnQueryObjectSet.All,
interactMask, solidMask, triggerMask,
CollisionGroup.Default,
out trace,
ignoreEntity1: pawn,
ignoreEntity2: null
);

SimpleTraceAngles

Like SimpleTrace but uses pitch/yaw angles instead of an end point:

CGameTrace trace;
Trace.SimpleTraceAngles(
origin, angles,
RayType_t.Line,
RnQueryObjectSet.All,
interactMask, solidMask, triggerMask,
CollisionGroup.Default,
out trace,
ignoreEntity1: pawn,
ignoreEntity2: null,
maxDistance: 5000f
);

Trace Methods Summary

MethodDescription
Ray(Vector3 start, Vector3 end, MaskTrace mask, CBaseEntity? ignore)Simple line ray, returns TraceResult
TraceShape(Vector3, Vector3, Ray_t, CTraceFilter, out CGameTrace)Full shape trace with custom ray and filter
SimpleTrace(...)Convenience with individual parameters
SimpleTraceAngles(...)Like SimpleTrace but with angles and max distance

TraceResult

Simplified result from Trace.Ray():

PropertyTypeDescription
DidHitboolWhether the ray hit something
HitPositionVector3World position of the hit
Fractionfloat0.0 (at start) to 1.0 (at end) — how far the ray traveled
TraceCGameTraceFull trace data

CGameTrace

Full result of a VPhys2 shape trace:

MemberTypeDescription
DidHitboolWhether the trace hit something
HitPointVector3World position of the hit
HitNormalVector3Surface normal at hit point
HitEntityCBaseEntity?Entity that was hit

Ray Shapes

RayType_t

Shape type for trace queries:

ValueDescription
LineLine ray (default)
SphereSphere shape
HullAABB hull
CapsuleCapsule shape

Shape Data Structs

StructDescription
LineTraceLine ray with optional radius (swept sphere)
SphereTraceSphere at a fixed center point
HullTraceAABB hull swept along a ray
CapsuleTraceCapsule between two center points with radius
MeshTraceConvex mesh with bounds and vertices

Collision Filtering

CollisionGroup

Determines which objects interact in physics simulation.

InteractionLayer

Individual content/interaction layers for building trace bitmasks.

MaskTrace

Bitmask combining InteractionLayer values:

ValueDescription
SolidSolid world geometry
(others)Various content layer combinations

RnQueryObjectSet

Controls which object sets are included:

ValueDescription
AllAll object sets (static, dynamic, locatable)

CTraceFilter

Full trace filter with entity-aware filtering:

var filter = new CTraceFilter(true) {
IterateEntities = true,
QueryShapeAttributes = new RnQueryShapeAttr_t {
ObjectSetMask = RnQueryObjectSet.All,
InteractsWith = MaskTrace.Solid,
InteractsExclude = MaskTrace.Empty,
InteractsAs = MaskTrace.Empty,
CollisionGroup = CollisionGroup.CitadelBullet,
HitSolid = true,
}
};
// Ignore specific entities by index (requires unsafe)
unsafe {
filter.QueryShapeAttributes.EntityIdsToIgnore[0] = (uint)pawn.EntityIndex;
}

RnQueryShapeAttr_t

Query attributes for shape traces:

PropertyTypeDescription
ObjectSetMaskRnQueryObjectSetWhich object sets to query
InteractsWithMaskTraceWhat the ray interacts with
InteractsExcludeMaskTraceWhat to exclude from interaction
InteractsAsMaskTraceWhat the ray acts as
CollisionGroupCollisionGroupCollision group for filtering
HitSolidboolWhether to hit solid objects
HitTriggerboolWhether to hit trigger volumes
EntityIdsToIgnorefixed uint[2]Up to 2 entity indices to skip (requires unsafe)
Trace.Ray hits trigger volumes

Trace.Ray with MaskTrace.Solid will hit trigger volumes like CPostProcessingVolume, returning DidHit=True with Fraction=0.000 if the ray starts inside one. For line-of-sight checks, use TraceShape with a CTraceFilter configured with CollisionGroup.CitadelBullet and HitSolid = true (without HitTrigger).

Example: Line-of-Sight Check

Bullet-style LOS check that ignores trigger volumes and specific entities:

var trace = CGameTrace.Create();
var ray = new Ray_t { Type = RayType_t.Line };
var filter = new CTraceFilter(true) {
IterateEntities = true,
QueryShapeAttributes = new RnQueryShapeAttr_t {
ObjectSetMask = RnQueryObjectSet.All,
InteractsWith = MaskTrace.Solid,
InteractsExclude = MaskTrace.Empty,
InteractsAs = MaskTrace.Empty,
CollisionGroup = CollisionGroup.CitadelBullet,
HitSolid = true,
}
};
unsafe {
filter.QueryShapeAttributes.EntityIdsToIgnore[0] = (uint)sourcePawn.EntityIndex;
filter.QueryShapeAttributes.EntityIdsToIgnore[1] = (uint)targetPawn.EntityIndex;
}
Trace.TraceShape(sourcePawn.EyePosition, targetPawn.EyePosition, ray, filter, ref trace);

bool hasLos = !trace.DidHit;
// trace.HitEntity?.Classname tells you what blocked (e.g. "CWorld" for walls)
note

EntityIdsToIgnore is a fixed-size buffer — accessing it requires unsafe code and <AllowUnsafeBlocks>true</AllowUnsafeBlocks> in your .csproj.

Example: Player Eye Trace

From the Deathmatch plugin:

[ChatCommand("trace")]
public HookResult OnTrace(ChatCommandContext ctx)
{
var pawn = ctx.Controller?.GetHeroPawn();
if (pawn == null) return HookResult.Handled;

var result = Trace.Ray(
pawn.EyePosition,
pawn.EyePosition + ForwardFromAngles(pawn.EyeAngles) * 5000f,
MaskTrace.Solid,
pawn
);

if (result.DidHit)
{
ctx.Controller.PrintToConsole($"Hit at {result.HitPosition}");
ctx.Controller.PrintToConsole($"Distance: {result.Fraction * 5000f} units");
}

return HookResult.Handled;
}

See Also