The solution I’d like to demonstrate allows to modify Navigation Mesh during game to allow NPC avoid high-cost areas.
This is how it looks in game (when debugging NavMesh):

The NavModifierVolume was added during a game after an NPC was killed in that area. After that the NPC does not want to come close to that area. In this video you can see how NPC behaves trying to avoid the deadly area (from 0:32 to 0:48):
By the way, modification of navigation mesh described in this article works closely with clustering that determined the deadly areas for NPC (read this article for details).
My Solution
In the essence, there are custom subclasses of ANavModifierVolume, UNavArea_Obstacle (each with its custom DefaultCost value), and my class UMBCG_NavSubsystem (UWorldSubsystem‘s subclass) as a manager.
During a game an instance of a custom NavModifierVolume is spawned with a custom NavArea_Obstacle specified and with the required dimensions. Thus, it modifies a NavMesh adding an area (NavMesh polygons) with a higher cost.
Classes:
- UMBCG_NavSubsystem: custom UWorldSubsystem class that manages custom NavModiferVolumes, spawns and destroys them.
- AMBCG_DeathPlaceNavModifierVolume: custom ANavModifierVolume class with BoxComponent which determines the dimensions of high-cost volume.
- UNavArea_Obstacle_TierXX_MBCG: custom UNavArea_Obstacle classes (where XX is from 01 to 08). Each of UNavArea_Obstacle_TierXX_MBCG determines a different DefaultCost.
Here is the structure of the solution:

C++ code
Here is the repository where you can find the mentioned custom classes.
See:
- MBCG_NavSubsystem.h
- MBCG_DeathPlaceNavModifierVolume.h
- NavArea_Obstacle_Tier01_MBCG.h, NavArea_Obstacle_Tier02_MBCG.h etc
Handled Challenges
- You can’t directly change the
DefaultCost
value of theNavModifierVolume
‘sNavArea_Obstacle
(and even if you try to do so by
->GetDefaultObject(), it will result into changing DefaultCost for all correspondingAreaClass
NavModifierVolume
objects where a particularNavArea_Obstacle
class is used.).
Instead, you need to create a newNavArea_Obstacle (or NavArea)
class with the desiredDefaultCost
and assign that new class to theNavModifierVolume
.
This is because theAreaClass
member in theANavModifierVolume
class is a reference to the class metadata, not an instance of theUNavArea
class. In other words,TSubclassOf
is a type-safe wrapper around aUClass*
, which refers to the metadata for a class in Unreal Engine, not an instance of the class itself. Therefore, you cannot modify instance-specific properties likeDefaultCost
directly throughAreaClass
.
Here are some code snippets for reference:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Default) TSubclassOf<UNavArea> AreaClass;
void FAreaNavModifier::Init(const TSubclassOf<UNavAreaBase> InAreaClass) { bExpandTopByCellHeight = false; bIncludeAgentHeight = false; ApplyMode = ENavigationAreaMode::Apply; Cost = 0.0f; FixedCost = 0.0f; Bounds = FBox(ForceInitToZero); SetAreaClass(InAreaClass); } void FAreaNavModifier::SetAreaClass(const TSubclassOf<UNavAreaBase> InAreaClass) { AreaClassOb = (UClass*)InAreaClass; UClass* AreaClass1 = AreaClassOb.Get(); UClass* AreaClass2 = ReplaceAreaClassOb.Get(); bHasMetaAreas = (AreaClass1 && IsMetaAreaClass(*AreaClass1)) || (AreaClass2 && IsMetaAreaClass(*AreaClass2)); }
My approach to it:
So I had to create multiple custom NavArea_Obstacle classes with required different DefaultCost values. It’s worth noting that DefaultCost values can be changed for a single NavArea_Obstacle during a game. It means that you need to have as many custom NavArea_Obstacle classes as many different DefaultCost values you need to have simultaneously at any time in your game.
In my case, I created eight custom UNavArea_Obstacle classes (from UNavArea_Obstacle_Tier01_MBCG to UNavArea_Obstacle_Tier08_MBCG) which is enough for my game.