Unity / September 1, 2022
RPG Builder Mod: Adding More Randomness to Patrol Paths
A Unity RPG Builder mod that lets NPCs choose from eligible patrol paths at random each time they return to the starting point.
Updated September 20, 2022
Intro
Without too much effort, we can extend the code in the previous post titled
RPG Builder Mod: Creating Reusable Patrol Path Prefabs, which gives an effective way of creating patrol prefabs that share
PatrolPath names, to also implementing random path choice support.
This feature allows NPCs to choose paths at random each time they return to the starting point of their path. By default RPGB will initialize a path at random when the game starts, and that will remain the NPCs path indefinitely, as can be seen in the out-of-the-box code below:
patrolPath = patrolTemplate.RandomPath ? GameObject.Find(patrolTemplate.PatrolPathNames[Random.Range(0, patrolTemplate.PatrolPathNames.Count)]).GetComponent<PatrolPath>()
: GameObject.Find(patrolTemplate.PatrolPathName).GetComponent<PatrolPath>(); It would be easy to add a toggle and either support the current, indefinite implementation, or this "choice-based" extension (irrespective of using my tag-based approach above), and I think people would love this as a default option in RPGB.
Check out the mod in action here:
You only need to do two things to get this effect:
-
If the user specified the path should be random, generate a list of eligible patrol paths
in the game scene. They must be in the
patrolPathNamesand they must be close to theAIEntity. -
In the
GetNextPoint()method you check if thePatrolPathsare meant to be random. If they are, then each time the NPC returns to theStartingPoint(pointIndex == 0), you select an eligible patrol path from Step 1 at random.
Disclaimer: There is no guarantee or future support offered regarding these
changes. I tested them out at the time I wrote them, and they worked within the scope of my
needs. However, I am not the author of RPGB and therefore am not the authoritative source on
whether these mods are complete. Use at your own risk. Reach out to me on Discord
(iNSiPiD1) if you have any thoughts or suggestions about this mod.
Modded RPGB v2.0.6. New code additions are always highlighted in green, whilst RPGB code is always highlighted in blue.
Mod AIStatePatrol.cs
We need to modify the code so that instead of picking one path at random and leaving it set to that until the NPC is destroyed, we maintain a list of eligible patrol paths for the mob to pick from each time they return to the starting point. That's what the code below does.
We're using LINQ so first include the proper using statement at the top of the file:
using System.Linq;
If you followed the previous post, then you replace the mod in the
Initialize() method there with the below code. If you didn't follow that post
then you replace the code in Initialize() with the green code below:
... (inside the Initialize() method around line 33)
patrolPath = patrolTemplate.RandomPath ? GameObject.Find(patrolTemplate.PatrolPathNames[Random.Range(0, patrolTemplate.PatrolPathNames.Count)]).GetComponent<PatrolPath>()
: GameObject.Find(patrolTemplate.PatrolPathName).GetComponent<PatrolPath>();
...
...
var patrolPathArray = GameObject.FindGameObjectsWithTag("NPCPatrolPath");
float[] patrolPathDistances = new float[patrolPathArray.Length];
for (int i = 0; i < patrolPathDistances.Length; i++)
{
patrolPathDistances[i] = Vector3.Distance(ThisAIEntity.transform.position,
patrolPathArray[i].transform.position);
}
if (patrolPathArray.Length == 0)
{
// Invoke RPGB default if no patrols are tagged and notify the console
Debug.Log("You didn't tag any patrol paths in this scene. Using default behavior.");
patrolPath = patrolTemplate.RandomPath ? GameObject.Find(patrolTemplate.PatrolPathNames[Random.Range(0, patrolTemplate.PatrolPathNames.Count)]).GetComponent<PatrolPath>()
: GameObject.Find(patrolTemplate.PatrolPathName).GetComponent<PatrolPath>();
}
else
{
if (patrolTemplate.RandomPath)
{
var eligiblePatrolDistances = new List<float>();
var randomIdx = Random.Range(0, patrolTemplate.PatrolPathNames.Count);
for (int i = 0; i < patrolPathArray.Length; i++)
{
if (patrolTemplate.PatrolPathNames.Contains(patrolPathArray[i].name))
{
if (patrolPathDistances[i] <= 1)
{
eligiblePatrolPaths.Add(patrolPathArray[i].GetComponent<PatrolPath>());
eligiblePatrolDistances.Add(patrolPathDistances[i]);
}
}
}
patrolPath = eligiblePatrolPaths[randomIdx];
}
else
{
var closestPatrolPathIndex = Array.IndexOf(patrolPathDistances, patrolPathDistances.Min());
patrolPath = patrolPathArray[closestPatrolPathIndex].GetComponent<PatrolPath>();
}
}
...
A little further down there's another method named GetNextPoint(). This method
figures out where the NPC should go next while it's on its path. We need to add the following
code at the bottom of the method, just before ThisAIEntity.StartMovement(); is
called.
... (bottom of GetNextPoint() method)
if (patrolTemplate.RandomPath & pointIndex==0)
{
patrolPath = eligiblePatrolPaths[Random.Range(0, patrolTemplate.PatrolPathNames.Count)];
}
ThisAIEntity.StartMovement();
MovementStateBlendCompleted = false;
}
The logic here is simple. If pointIndex == 0 and the user specified they want
random paths, then select one from one of the cached patrol paths.
The End
That's it! Once you make these code changes your NPC will select a path at random from the patrol paths they are set up to use.