Spatial and games
Tracer: pathfinding and game AI for .NET
Pathfinding is the easy part. Tracer does the rest.
- Release 2.0.2
- Triangle navmesh and a bake-free tile lane
- Self-tuning concurrency
- net8.0 and net10.0
dotnet add package EvaluatedApplications.TracerCommercial licence for commercial use. Evaluation and non-commercial use are permitted.Licence terms
The field
A baked level, fourteen agents and the paths Tracer returned. Switch the view to close a doorway, compare single queries with the swarm tick, or flood a tile range. Every line is package output.
Drag the view or use the arrow keys while it is focused to turn and tilt it.
14 agents head for 2 goals across two walls with two doorways each. Agents that share a goal share one solve. 14 of 14 paths found.
- Agents
- 14
- Paths found
- 14 of 14
- Distinct goals
- 2
- Mesh triangles
- 788
Data behind the diagram
| View | Paths found | Mean waypoints | Total length (cells) | Agents re-routed | Deepest corner cut (cells) |
|---|---|---|---|---|---|
| Swarm tick | 14 of 14 | 10.5 | 343.6 | none | 0.50 |
| Doorway closed | 14 of 14 | 12.7 | 373.6 | 12 of 14 | |
| Single queries | 14 of 14 | 6.7 | 339.5 | none | 0.11 |
| Agent | Start | Goal | Swarm waypoints | Swarm length | Closed waypoints | Closed length | Re-routed | Single waypoints | Single length |
|---|---|---|---|---|---|---|---|---|---|
| Agent 1 | (1.5, 1.5) | (28.5, 8.5) | 9 | 29.4 | 13 | 32.5 | yes | 7 | 29.4 |
| Agent 2 | (3.5, 2.5) | (28.5, 8.5) | 9 | 27.0 | 13 | 30.1 | yes | 7 | 27.0 |
| Agent 3 | (5.5, 1.5) | (28.5, 8.5) | 9 | 25.9 | 16 | 29.8 | yes | 7 | 25.4 |
| Agent 4 | (7.5, 3.5) | (28.5, 8.5) | 9 | 23.4 | 16 | 27.0 | yes | 6 | 22.6 |
| Agent 5 | (2.5, 5.5) | (28.5, 8.5) | 10 | 27.9 | 13 | 31.1 | yes | 7 | 28.1 |
| Agent 6 | (6.5, 5.5) | (28.5, 8.5) | 10 | 23.7 | 13 | 28.0 | yes | 6 | 24.1 |
| Agent 7 | (9.5, 6.5) | (28.5, 8.5) | 9 | 20.4 | 13 | 26.6 | yes | 8 | 21.2 |
| Agent 8 | (1.5, 8.5) | (28.5, 8.5) | 12 | 29.8 | 12 | 30.0 | yes | 8 | 28.8 |
| Agent 9 | (4.5, 9.5) | (28.5, 8.5) | 12 | 26.4 | 12 | 26.6 | yes | 7 | 25.9 |
| Agent 10 | (7.5, 8.5) | (28.5, 8.5) | 12 | 24.7 | 12 | 25.2 | yes | 6 | 24.0 |
| Agent 11 | (2.5, 11.5) | (28.5, 8.5) | 11 | 29.4 | 11 | 29.4 | no | 7 | 27.2 |
| Agent 12 | (5.5, 12.5) | (28.5, 8.5) | 13 | 25.0 | 12 | 26.7 | yes | 7 | 24.9 |
| Agent 13 | (8.5, 13.5) | (28.5, 8.5) | 14 | 22.7 | 14 | 22.6 | yes | 7 | 22.9 |
| Agent 14 | (10.5, 8.5) | (16.5, 13.5) | 8 | 8.0 | 8 | 8.0 | no | 4 | 7.9 |
| Measure | Value |
|---|---|
| Tiles reachable within 7 steps | 96 |
| Tiles in line of sight | 179 |
| Reachable tiles beside a wall | 14 |
What Tracer is
Tracer is a pathfinding and game-AI SDK for real-time simulations with many moving agents: crowds, RTS-style unit groups and tactical squads. It bakes triangle navmeshes, routes single agents and whole swarms across them, reacts to obstacles that appear or vanish mid-game, and adds the game-AI toolkit around pathfinding: line of sight, fog of war, influence maps and combat engagement.
A swarm costs close to one agent
Agents that share a goal share one solve, so five hundred agents heading to the same point cost close to one. Adaptive path budgeting keeps large swarms inside a frame-time budget under pressure.
Obstacles change without a re-bake
Block and unblock regions of a baked mesh while the game runs. The mesh handle is immutable, so you swap the new one in between ticks.
Everything around the path
Line of sight, fog of war, influence maps, congestion analysis, steering behaviors and combat resolution ship in the same SDK, on the same pipeline runtime.
Quick start
Bake a small room with a doorway, query a path, run a swarm, then close the doorway.using System.Numerics;
using Tracer;
using Tracer.Models;
var world = new PathfindingWorld();
// A 6x4 room with a wall down the middle and a doorway in it.
// Two triangles per walkable cell; vertices sit on the integer lattice.
var vertices = new List<Vector2>();
var triangles = new List<(int A, int B, int C)>();
var lattice = new Dictionary<(int, int), int>();
int V(int x, int y)
{
if (!lattice.TryGetValue((x, y), out var i)) { i = vertices.Count; lattice[(x, y)] = i; vertices.Add(new(x, y)); }
return i;
}
for (int y = 0; y < 4; y++)
for (int x = 0; x < 6; x++)
{
bool wall = x == 3 && y != 1; // the doorway is the cell (3, 1)
if (wall) continue;
triangles.Add((V(x, y), V(x + 1, y), V(x + 1, y + 1)));
triangles.Add((V(x, y), V(x + 1, y + 1), V(x, y + 1)));
}
var mesh = await world.CreateNavMeshBakerService().BakeAsync(vertices, triangles);
Console.WriteLine($"triangles: {mesh.TriangleCount}");
// One smoothed path.
var path = await world.CreateSingleAgentService()
.QueryPathAsync(mesh, new Vector2(0.5f, 3.5f), new Vector2(5.5f, 3.5f));
Console.WriteLine($"single path: {path.Count} waypoints, ends at {path[^1]}");
// A swarm sharing one goal is solved once.
var goal = new Vector2(5.5f, 0.5f);
var agents = Enumerable.Range(1, 3)
.Select(i => new AgentDto(Id: i, Position: new Vector2(0.5f, i - 0.5f), Goal: goal, GoalChanged: true))
.ToList();
var tick = await world.CreateMultiAgentService().TickAsync(agents, mesh, tickNumber: 0);
Console.WriteLine($"swarm: {tick.Paths.Count(p => p.PathFound)} of {agents.Count} agents have a path");
// Close the doorway at runtime. No re-bake.
var blocked = await world.CreateDynamicObstacleService()
.UpdateAsync(mesh, new[] { new NavMeshBlockedZone(Center: new Vector2(3.5f, 1.5f), Radius: 0.6f) });
var after = await world.CreateSingleAgentService("after")
.QueryPathAsync(blocked, new Vector2(0.5f, 3.5f), new Vector2(5.5f, 3.5f));
Console.WriteLine($"doorway closed: path found = {after.Count > 0}");triangles: 42
single path: 4 waypoints, ends at <5.5, 3.5>
swarm: 3 of 3 agents have a path
doorway closed: path found = FalseThis is the complete program, compiled and run against the published package.
Capabilities
Navmesh baking
Turn triangle geometry into a queryable navmesh, with obstacle-polygon exclusion and an optional hierarchical bake for large levels, or layered meshes for ground, air and water.
Single and multi-agent paths
One-shot smoothed paths, batches in parallel, or a swarm tick where agents with the same goal reuse one solve.
Dynamic obstacles
Block and unblock circular zones of a baked mesh at runtime without a full bake.
Line of sight, fog of war, influence maps
Visibility and area-control queries for perception and decision-making, with congestion analysis alongside.
Combat engagement
Spatially hashed attacker and defender resolution with pluggable damage, dodge and block formulas.
Steering behaviors
Seek, Wander, Patrol, Flee, Queue and FlowLane, composed through a fluent navigator builder.
Grid tactics
A separate bake-free lane for tile games: line of sight, movement range, cover and target selection over any tilemap.
Adaptive budgeting and populations
Cluster large agent counts and path only representatives when the frame budget is tight, or run several named populations under one shared budget.
Limits
What Tracer does not do, and where to be careful- The navmesh is made of triangles, not tiles. A tile game bakes two triangles per walkable cell, or uses the bake-free tile lane.
- An unreachable or off-mesh goal never throws. A single query returns an empty list, and a swarm result reports that no path was found.
- Blocking a zone at runtime isolates triangles in place, so the triangle count does not change. Baking with blocked zones excludes them instead.
- Pressure is a meaningful signal only when a frame-time target is set.
- Swarm waypoints are shared guidance. On the level in the diagram they cut doorway corners by up to 0.50 of a cell; single-agent queries stay within 0.11 of a cell. Use single queries where exact corner clearance matters.
- Mesh handles are immutable and safe to read from any thread. Services that persist tuner state should not be driven concurrently under one service key.
Every public type is documented with a worked example in the package readme on NuGet. Open the NuGet page.
Related
Licence
Proprietary. Every package ships as a compiled library. Evaluation and non-commercial development or testing are permitted; commercial, production or revenue-generating use needs a commercial licence from the owner. Read the licence terms.