LCM Nav3D Free demo Get it on Fab

Tutorial 10 - 3D NavLinks + volumetric NavArea modifiers

TL;DR: LCM Nav3D ships ULcmNavLinkComponent for vertical ladders/jump shafts/teleporters (3D-advantage over Recast's surface-projected links) and ALcmNavAreaModifier for volumetric cost overlays (storm clouds, water volumes, danger zones, preferred lanes). Both auto-register on BeginPlay; query via ULcmNavAreaQueryLibrary from BP or C++.

The 3D-advantage angle

Unreal's stock UNavLinkCustomComponent is fine for flat NavLinks projected onto the Recast navmesh. But:

LCM Nav3D flying pawns can use any two world-space points for the link endpoints. The components below surface that capability so designers don't have to hand-roll a 3D-aware link system.

  1. In the editor, spawn an empty actor (or reuse ANavLinkProxy).
  2. Add Component → search "LCM Nav Link (3D Vertical)".
  3. Set on the component:
    • LinkStart = world position of the ladder bottom (e.g. (0, 0, 0))
    • LinkEnd = world position of the ladder top (e.g. (0, 0, 800) - 8 m vertical)
    • bBidirectional = true (climb up or slide down)
    • bAutoRegisterWithApex = true (default)
    • TraversalCostCm = extra cm-cost of using the ladder (0 = free; e.g. 400 to make agents prefer a nearby ramp). Queryable in v1 (see Step 3); auto-routing is v2.

That's it. The link is now visible to:

Step 2 - Drop a volumetric NavArea modifier (water example)

  1. In the editor, spawn a ALcmNavAreaModifier actor.
  2. Move + scale the box gizmo to cover your water volume.
  3. Set:
    • CostMultiplier = 3.0 (agents prefer dry routes by 3× cost)
    • AreaTag = "Water" (optional designer label)
    • bAutoRegisterWithApex = true

Stack multiple modifiers in the same volume - costs multiply:

Volume Cost multiplier
Water 3.0
+ storm cloud overlapping × 5.0
Effective in overlap region 15.0

Step 3 - Query from BT / StateTree / Blueprint

// In a BT service tick, or anywhere with C++ access:
const float Cost = ULcmNavAreaQueryLibrary::GetCostMultiplierAt(AgentLocation);

// Or evaluate a planned path's accumulated area cost (multiplicative):
const float PathCost = ULcmNavAreaQueryLibrary::GetCostMultiplierAlongPath(Waypoints);

//.and the additive cm-cost of any priced NavLinks the route uses:
const float LinkCost = ULcmNavAreaQueryLibrary::GetNavLinkTraversalCostAlongPath(Waypoints);
// Combine however your AI weighs routes, e.g. compare two candidate paths and pick
// the one with the lower (length * PathCost + LinkCost).

// Find the nearest ladder for "approach the ladder" patterns:
ULcmNavLinkComponent* Nearest =
    ULcmNavAreaQueryLibrary::FindNearestNavLink(AgentLocation, /*MaxDist*/ 500.0f);
if (Nearest)
{
    FVector LadderStart, LadderEnd;
    ENavLinkDirection::Type Dir;
    Nearest->GetLinkData(LadderStart, LadderEnd, Dir);
    // Have FlyTo task move to LadderStart, then trigger the link traversal.
}

All of these are Blueprint-callable via the standard "LCM Navigation | Bridge" category.

Solver integration (v1 vs v2)

v1 (this release):

v2 (deferred):

v1 is the placement + registry + query surface; v2 is the solver-side cost integration. The data plane is ready; the planner-side wiring lands when the Marketplace v3.0 ramp justifies the solver-touch budget.

Common patterns

Ladder + drop-down combo

Two ULcmNavLinkComponents on the same actor:

Storm cloud + ground danger

Two ALcmNavAreaModifiers:

Agents flying at Z=200 see normal cost; flying at Z=400 see 5× cost; flying through ground combat at Z=50 see 10× cost.

Preferred lane

Single ALcmNavAreaModifier with CostMultiplier = 0.5. LCM Nav3D paths bias toward this volume when the v2 solver integration lands.

Acceptance test (manual)

  1. Drop a ULcmNavLinkComponent with LinkStart at (0,0,0), LinkEnd at (0,0,500), and TraversalCostCm = 400.
  2. In a Blueprint test actor: Print String (ULcmNavAreaQueryLibrary::GetRegisteredLinkCount) → should print 1.
  3. Move the test actor near (0,0,0). Call FindNearestNavLink(actor.location, 1000) → returns your component.
  4. GetNavLinkTraversalCostAlongPath([(0,0,0),(0,0,500)]) → returns 400. A path that stays away from the link (e.g. [(5000,0,0),(6000,0,0)]) → returns 0.
  5. Drop a ALcmNavAreaModifier covering (0,0,250) with CostMultiplier 5.0.
  6. GetCostMultiplierAt((0,0,250)) → returns 5.0. At (0,0,800) → returns 1.0.