Hello AI developers,
Currently, PathfindingService almost always returns just the shortest path between the source and the target points. However, the optimal path should often consider the character/agent abilities, terrain type and game rules.
For example - you may want an NPC to travel over a bridge instead of swim through water, cross a street in the crosswalk, or avoid an area that would damage the character.
Introducing PathfindingModifiers
PathfindingModifiers are instances used to annotate/modify parts or meshparts. Their purpose is to let game creators customize the cost of traversing the volume their parent defines. To do so, CreatePath() now also includes a parameter Costs
to map PathfindingModifier.ModifierId
to cost multipliers. For instance, a cost value of 100.0 makes the parent Part 100.0 times harder to traverse. Conversely, a cost value of 0.5 makes the volume twice as likely to be included in the final path.
Let’s review some practical use cases of this feature.
1. Can the agent step over this brick?
When writing the AI for NPC’s in a game with death bricks, it’s useful to let PathfindingService know that the agent wants to avoid them, instead of walking over them. Annotating the part with a PathfindingModifier
instance lets us customize the resulting path, like so:
local modifier = Instance.new("PathfindingModifier")
modifier.ModifierId = "DeathBrick"
modifier.Parent = deathBrickPart
local agentParameters = {
AgentRadius = 2,
AgentCanJump = True,
Costs = {
DeathBrick = 10.0
}
}
local path = PathfindingService:CreatePath(agentParameters)
path:ComputeAsync()
The new Costs parameter maps the identifier(s) to a cost multiplier that is used during the path search to establish priorities for crossing over that region. The constant math.huge
can be used to indicate the region should be avoided altogether, even if it is the only path available.
2. Don’t cross the street in the middle of the block!
Let’s say we are working on a city simulation game and we populate the streets with pedestrians that use crosswalks as much as possible. Splitting the street model into crosswalks/non-crosswalks might not be practical. To annotate arbitrary regions, PathfindingModifiers
can be parented to volumes. Volumes are usually parts that are non-collidable, anchored, and transparent, used to establish a region for gameplay purposes. As a result, the modifier will annotate any world geometry that overlaps with it.
Using the code below, you can place an invisible part that indicates where the crosswalk is on the map.
local modifierVolume = Instance.new("Part", workspace)
modifierVolume.CanCollide = false
modifierVolume.Anchored = true
modifierVolume.Transparency = 1.0
modifierVolume.Position = ...
modifierVolume.Size = ...
local modifier = Instance.new("PathfindingModifier", modifierVolume)
modifier.ModifierId = "Crosswalk"
local agentParameters = {
Costs = {
Crosswalk = 0.01
}
}
3. Noobs can’t swim!
PathfindingService
will automatically annotate regions with parts and terrain materials.
In the following example we include a high cost multiplier for water, which steers the NPC to the bridge when a bridge is present as an alternative path.
There’s no set rule on what the multiplier value should be - it should be tuned until the desired effect is achieved. The distance from the bridge and the amount of water that needs to be crossed will be factored into the weighting of the final path.
local agentParameters = {
Costs = {
Water = 100.0
}
}
4. Open the gates
PathfindingService
sees all obstacles initially as having the same “cost”. The service doesn’t know how to differentiate a brick wall from an unlocked door. With PathfindingModifiers
it is possible to set a priority for an entryway like a door.
The following example enables the PassThrough
property in the modifier to indicate that all of the obstacles enclosed by it are traversable (in this case, the door that can be opened).
doorVolume.PathfindingModifier.ModifierId = "Door"
doorVolume.PathfindingModifier.PassThrough = true
local canCrossDoor = true or false
local agentParameters = {
Costs = {
Door = canCrossDoor and 1.0 or math.huge
}
}
How to enable PathfindingModifiers
You’ll need to enable the Beta Feature first.
To enable the beta feature:
- Go to File → Beta Features in Studio
- Check the box to the left of “Pathfinding Modifiers”
- Restart Studio
You can read more about Pathfinding and Modifiers on the DevHub:
Debug visualization
It is possible to see what annotations are used by the service. This especially useful when models and terrain overlap and it’s not clear what an agent will see.
To enable it, open the Studio settings and enable the following settings:
FAQ
What is the priority in resolving normal parts, terrain, and parts with modifiers?
- The priority is: PathfindingModifier → Part Material → Terrain Material
What if several parts/volumes overlap with each other?
- We suggest you avoid this where possible; the results are unpredictable
Known Issues
- Parts that are close to the surface of terrain will sometimes recognize the terrain material instead of the part sitting on top (fix is in progress)
We want to hear your feedback
We are excited to make this beta available to you. Let us know what you think and send us cool uses you come up with for the feature.
Special thanks to @Cinderstock for engineering work on this feature.