[EDITED FOR A THIRD TIME]
Brief overview of the problem:
Created a simple waypoint script attached to an npc with a character controller component. It rotates him towards the current waypoint, moves him towards it and when he's within a certain radius of it, tells him to aim at the next waypoint. When he gets to the end he aims for the first waypoint again.
Later added functionality to have the NPC patrol back and forth between a series of points or go round the waypoints in a loop. A boolean determines which behaviour this NPC performs. Worked perfectly at first, but then a problem appeared: When the npc reaches the first waypoint he just rotates around and around and doesn't select the next waypoint as his target.
Based on SpinalJack and Johan's answers, the problem seems to be that the NPC is overshooting the waypoint radius. Making a bigger radius works, but i'd rather implement Johan's suggestion re. the Dot Product as this should be more accurate.
Update
I couldn't get it to work using the Dot Product. To be honest, I didn't really understand what it was doing. Looking at Johan's suggestion again, it seems using the Dot Product and squaring the distance are two different solutions, rather than two I should implement simultaneously? I say this because squaring the distance successfully stops the spinning problem.
It seems though, that all this is doing is creating a bigger radius to reduce the risk of the npc overshooting? If that's true, then it's not really any different than setting a bigger radius to begin with. It still seems to break at sizes less than my moveSpeed variable (3), so i'm thinking that setting my radius to my moveSpeed variable will make it work in any situation (even if my npc moves at different speeds)?
That's the approach i've taken below, the squared radius coming out as 9m with the default settings. Both the LoopWaypoints and PatrolWaypoints functions work but there does seem to be quite a bit of duplicate code in there that i'd like to tidy up, if anyone has any ideas? There's also a noticeable delay in both when reaching the end of the waypoints and deciding where to go next.
var waypoints = new Array();
var moveSpeed = 3.0;
var rotationSpeed = 3.0;
var waypointLoop : boolean = false;
var myWaypoints : Transform;
private var currentWaypoint : int;
private var returning : boolean;
private var forward;
private var moveDirection : Vector3;
var waypointDirection : Vector3;
var waypointDistance : float;
var waypointRadius : float = moveSpeed;
function Start(){
for (var child : Transform in myWaypoints) {
waypoints.Add(child);
}
currentWaypoint = 0;
returning = false;
}
function Update(){
forward = transform.TransformDirection(Vector3.forward);
moveDirection = forward * moveSpeed;
if(waypoints.length<=0){
print("WARNING!! - No waypoints have been set for "+gameObject.name);
} else {
if(waypointLoop){
LoopWaypoints();
} else {
PatrolWaypoints();
}
}
}
function LoopWaypoints(){
if(currentWaypoint < waypoints.length){
waypointDirection = waypoints[currentWaypoint].position - transform.position;
waypointDistance = waypointDirection.sqrMagnitude;
if(waypointDistance <= (waypointRadius * waypointRadius)){
currentWaypoint++;
} else {
RotateTowards(waypointDirection);
MoveForward();
}
} else {
currentWaypoint = 0;
}
}
function PatrolWaypoints(){
if(!returning){
if(currentWaypoint < waypoints.length){
waypointDirection = waypoints[currentWaypoint].position - transform.position;
waypointDistance = waypointDirection.sqrMagnitude;
if(waypointDistance <= (waypointRadius * waypointRadius)){
currentWaypoint++;
} else {
RotateTowards(waypointDirection);
MoveForward();
}
} else {
currentWaypoint--;
returning = true;
}
} else {
if(currentWaypoint > 0){
waypointDirection = waypoints[currentWaypoint].position - transform.position;
waypointDistance = waypointDirection.sqrMagnitude;
if(waypointDistance <= (waypointRadius * waypointRadius)){
currentWaypoint--;
} else {
RotateTowards(waypointDirection);
MoveForward();
}
} else {
returning = false;
}
}
}