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 patrolPathNames and they must be close to the AIEntity).
In the GetNextPoint() method you check if the PatrolPaths are meant to be random. If they are, then each time the NPC returns to the StartingPoint (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.
First since we're using linq make sure you have the proper using at the top of the file:
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
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.
Comments