Tutorial 10 - Cost areas, query filters and 3D NavLinks
TL;DR: Drop an ALcmNavAreaModifier
volume, give it a stock NavArea class, and every LCM Nav3D planner
routes by it: FlyTo (BT and StateTree), Fly To Location, Mass crowds,
FindPath and the stock Move To. A stock
NavigationQueryFilter on the agent decides what each area
costs that agent, or forbids it.
ULcmNavLinkComponent places 3D ladders, drops and
teleporters; link costs are queryable, and the planner does not route
through links yet.
What an area does
A cost area is a volume (box, sphere or capsule) with a NavArea class. The planner reads three things from the class:
| NavArea field | Effect on a route |
|---|---|
| Default Cost | Travel inside the area costs this many times the distance.
5.0 = five times dearer, 0.5 = a preferred
lane. |
| Fixed Area Entering Cost | Paid once each time the route goes in. Use it for a toll or a doorway you want used sparingly. |
Default Cost at NavArea_Null's level (1e30 or
more) |
Impassable for everyone. No route crosses it. |
A query filter on an agent overrides those per class, exactly as it does for Recast: a travel cost override, an entering cost override, or excluded. Two agents read the same volume differently. The prey avoids a predator's territory that the predator itself ignores.
Routes follow the cheapest total, not the shortest distance. An agent goes round a storm when going round is cheaper, and flies straight through when the detour costs more than the storm.
Step 1 - Place a cost area
- Place an LCM Nav3D Nav Area Modifier (Volumetric) actor.
- Set Shape and scale the box to cover the volume. A sphere reads its radius from the extent's X. A capsule runs along local Z.
- Set Area Class. The plugin ships ready-made
classes, and any stock or project
NavAreasubclass works:
| Class | What it does |
|---|---|
LCM Nav Area - Storm (x5) |
Five times dearer inside. |
LCM Nav Area - Preferred Lane (x0.5) |
Half price: routes are drawn into it. |
LCM Nav Area - Toll Gate (+2000 to enter) |
Normal price inside, 2000 cm extra per entry. |
LCM Nav Area - Territory (priced by filters) |
Free for everyone, until a filter prices it. |
LCM Nav Area - Restricted (excluded by filters) |
Free, until a filter excludes it. |
LCM Nav Area - No-Fly (impassable) |
Nobody crosses it, under any filter. |
Leave Area Class empty to use Cost Multiplier on its own. That is the 3.3 behaviour, with no per-agent overrides.
Overlaps. Where volumes overlap, an area the agent's filter excludes always wins. Otherwise the higher Priority decides the cost. Costs do not multiply. (They did before 3.4.)
Moving areas. Attach the volume to anything that moves and tick Movable. Agents replan as it moves, and plan against where it will be over Prediction Seconds. Patrol moves a volume back and forth on its own, for demos and tests. After changing a class, shape, priority or size at runtime, call Notify Area Changed. Moving the actor needs no call.
Step 2 - Give an agent a filter
A filter is a stock NavigationQueryFilter Blueprint or
class. Add Areas entries to override or exclude
classes. The plugin ships two examples:
LCM Nav Filter - Prey (Territory x10) and
LCM Nav Filter - Civilian (Restricted excluded).
| Agent | Where the filter goes |
|---|---|
| Pawn with an AI controller (BT, StateTree, Fly To Location) | The controller's Default Navigation Filter Class. FlyTo reads it on every request. No task property is needed. |
| A direct planner call | FLcmNavigationQueryOptions::FilterClass. |
| Mass crowd | Navigation Filter on the LCM Nav3D Agent trait. |
| Stock Move To | Its Filter Class pin, as with Recast. |
With no filter, each area uses its own class defaults.
Step 3 - See what an agent sees
On the LCM Nav3D Manager, under 6. Debug > Cost Areas:
- Cost View: Regions draws each volume and what it costs. Heat tints the octree by price.
- Cost View Filter: whose eyes the view looks through. Set it to the prey's filter to see the territory light up.
The console variable r.LcmNav.CostView 0|1|2 overrides
the property while playing. The Gameplay Debugger's LCM category shows
the selected agent's route cost: length, priced length, entering costs
paid, and the areas it crosses.
What happens at the edges
| Situation | Result |
|---|---|
| The goal is inside an area the agent's filter excludes | The request fails: Get Last Path Cost Failure returns
Goal In Excluded Area. |
| Every way to the goal crosses an excluded area | The request fails: No Route Under Filter. |
| The agent starts inside an excluded area | It is allowed out by the shortest way, then routes normally. |
| An area moves across an agent's route | FlyTo replans when the route's cost changes enough to matter. |
A FlyTo task reports these as Failed, with a Warning in the log, never an Error.
Step 4 - Query costs yourself
The query library prices a route the same way the planner does:
// The travel multiplier at a point, and a route's cost, as a given filter sees them:
bool bExcluded = false;
const float Here = ULcmNavAreaQueryLibrary::GetCostMultiplierAtForFilter(this, Location, UMyPreyFilter::StaticClass(), bExcluded);
float LengthCm, PricedCm, EnteringCost; bool bCrossesExcluded;
ULcmNavAreaQueryLibrary::EvaluatePathCostForFilter(this, Waypoints, UMyPreyFilter::StaticClass(),
LengthCm, PricedCm, EnteringCost, bCrossesExcluded);
The filter-less versions (GetCostMultiplierAt,
GetCostMultiplierAlongPath) read each area's own defaults.
All of them are Blueprint-callable.
3D NavLinks
UE's stock UNavLinkCustomComponent projects its ends
onto a Recast navmesh. A flying pawn can use any two points in space, so
LCM Nav3D ships ULcmNavLinkComponent for vertical ladders,
drops and teleporters.
- Add the LCM Nav Link (3D Vertical) component to an actor.
- Set
LinkStartandLinkEndin world space, e.g.(0, 0, 0)and(0, 0, 800). bBidirectional: climb up and slide down, or one way only.TraversalCostCm: the extra cost of using the link, e.g.400to favour a nearby ramp.
The link registers with UE's navigation system (it implements
INavLinkCustomInterface) and with LCM Nav3D's registry.
The LCM Nav3D planner does not route through links yet.
Find and price them yourself:
ULcmNavLinkComponent* Nearest = ULcmNavAreaQueryLibrary::FindNearestNavLink(AgentLocation, /*MaxDist*/ 500.0f);
const float LinkCost = ULcmNavAreaQueryLibrary::GetNavLinkTraversalCostAlongPath(Waypoints);
A common pattern: FlyTo to LinkStart, play the
traversal, then continue from LinkEnd.
Try it
Open the Cost Areas tile in the demo menu
(NavCost_Showcase maps, in BT, StateTree and Mass
versions). Six labelled stations: a storm the route bends round, a
preferred lane off the direct line, a no-fly goal beside a storm goal
(impassable is not the same as expensive), one territory that only the
prey pays for, a toll gate, and two craft of different sizes making the
same decision.
Acceptance test (manual)
- Place a
Storm (x5)box between two points, with open space around it. - Fly an agent across. It goes round.
- Stretch the storm wall to wall so there is no way round. The agent flies through it and pays the storm.
- Give the agent the
Civilianfilter and put aRestrictedbox across the only corridor to its goal. The request fails with No Route Under Filter. GetRegisteredLinkCountcounts yourULcmNavLinkComponents.GetNavLinkTraversalCostAlongPath([(0,0,0),(0,0,500)])returns the link'sTraversalCostCm.