A* Pathfinding Project  3.7
The A* Pathfinding Project for Unity 3D
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Events Macros Groups Pages
Writing Modifiers

Modifiers are small scripts which post-process paths to for example simplify or smooth them.


They are built into the system using extendable add-on architecture which means that it is easy to add your own modifier.
In this tutorial I will show you how to write a simple path smoother similar to the one included in the project (Components–>Pathfinding–>Modifiers–>SimpleSmooth)

New Script

Begin by creating a new C# script somewhere in your project, name it ModifierTutorial.
Open it up in your favourite script editor, you will have a basic class looking something like this:

using UnityEngine;
using System.Collections;
public class ModifierTutorial : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}

Now, we are going to make a modifier which can be attached to any GameObject with a Seeker, to do that we need to inherit from the MonoModifier class.
This class will handle basic communication between the Seeker and the Modifier and will greatly help the writing of the modifier.
It is an abstarct class, so some functions need to be implemented in our modifier to not throw compiler errors:

using UnityEngine;
using System.Collections;
using Pathfinding;
[System.Serializable]
public class ModifierTutorial : MonoModifier {
public override ModifierData input {
get { return ModifierData.VectorPath; }
}
public override ModifierData output {
get { return ModifierData.VectorPath; }
}
public override void Apply (Path p, ModifierData source) {
}
}

What we have here now is the most basic modifier... which doesn't really do anything, but it will serve as a template for future writing of modifiers.
I have added the "using Pathfinding" statement because MonoModifier exists in the Pathfinding namespace, so we need to include it in our script.
There are also the input and output properties, these tell the Seeker what this modifier accepts and what it outputs. Usually you take a VectorPath as input and output a VectorPath, see Pathfinding::ModifierData for more info.
The Apply function is where we are going to put our code, it will be called when a path needs post-processing.
The Path object supplied is the path which we are going to post-process and the source is what the previous modifier outputed, but we don't need to care about it really since it is guaranteed to be ModifierData.VectorPath as we set that as input.

Smoothing the Path

The smoothing algorithm we are going to use is quite simple, it should just draw the points closer together by some amount, and we are going to work on the Pathfinding::Path::vectorPath array, the Pathfinding::Path::path array should never be changed as it might break other modifiers.
First though we need to check if the path suceeded and if the vectorPath array is not null, otherwise we might end up with evil NullReferenceExceptions
And also, how are we going to smooth the path if there is less than three elements in it, it can't be done, so we will skip that case too.

public override void Apply (Path p, ModifierData source) {
if (p.error || p.vectorPath == null || p.vectorPath.Length < 3) {
return;
}
}

Then for the actual smoothing, the algorithm will work as follows:
subdivide the path into smaller segments, then loop through the path array, and move each point except the first and the last ones closer to it's adjacent points, do that a number of times to get the desired smoothing.

//The number of times to apply smoothing
public int iterations = 5;
//The number of times to subdivide the original path
public int subdivisions = 2;
public override void Apply (Path p, ModifierData source) {
if (p.error || p.vectorPath == null || p.vectorPath.Length < 3) {
return;
}
//Subdivisions should not be less than zero
subdivisions = subdivisions < 0 ? 0 : subdivisions;
//Prevent unknowing users from entering bad values
if (subdivisions > 12) {
Debug.LogWarning ("Subdividing a path more than 12 times is quite a lot, it might cause memory problems and it will certainly slow the game down.\n" +
"When this message is logged, no smoothing will be applied");
subdivisions = 12;
return;
}
//Create a new array to hold the smoothed path
Vector3[] newPath = new Vector3[(p.vectorPath.Length-1)*(int)Mathf.Pow(2,subdivisions)];
Vector3[] originalPath = p.vectorPath;
//One segment (line) in the original array will be subdivided to this number of points
int segLength = (int)Mathf.Pow(2,subdivisions);
for (int i=0;i<originalPath.Length-1;i++) {
float oneDivLength = 1F / segLength;
for (int j=0;j<segLength;j++) {
//Use Vector3.Lerp to place the points at their correct positions along the line
newPath[i*segLength + j] = Vector3.Lerp (originalPath[i],originalPath[i+1],j*oneDivLength);
}
}
//Assign the new path to the p.vectorPath field
p.vectorPath = newPath;
//Smooth the path [iterations] number of times
for (int it=0;it < iterations;it++) {
//Loop through all points except the first and the last
for (int i=1;i<p.vectorPath.Length-1;i++) {
//Set the new point to the average of the current point and the two adjacent points
Vector3 newpoint = (p.vectorPath[i] + p.vectorPath[i-1] + p.vectorPath[i+1]) / 3F;
p.vectorPath[i] = newpoint;
}
}
//Assign the last point
p.vectorPath[p.vectorPath.Length-1] = originalPath[originalPath.Length-1];
}

Note that the new path gets assigned to the p.vectorPath field, that will enable other scripts to find it.

The End

That was the end of this tutorial, I hope it will help you get started writing path modifiers.