AI with random waypoint

Script function : Tell a NPC to choose waypoint destination randomly depending on which sector he’s attached to. While waypoint is reached, wait for a moment. If a NPC is near to collide with another NPC, change his direction a bit to not collide with it.

Feel free to copy it and modify it to a better AI!
if possible, just write updated changes in it to help creating a better random waypoint AI.

Under Construction!

First update:
ok, after multiple attempt its finally working. The only thing is that when they enter the “Evitate” state, the movement of the NPC is quite ugly, always moving up for 1.5f seconds.
I want to change his direction depending on which way is the other NPC going. If you wanna give me hint, i accept all of them hehe

This is just temp version :slight_smile:

using UnityEngine;
using System.Collections;

public class WaypointAnimator : MonoBehaviour
{
private enum WaypointState { MovingToNextWaypointPoint = 0, WaitingForNextMove = 1, Evitate = 2 } //State of NPC

public int StartupWaypointIndex = 0;                //Waypoint to go at beginning
public int Sector = 1;                              //tag waypoint waypointSector + sector to tell NPC which one of sector to follow
public float WaypointWalkSpeed = 1.25f;             //Walkspeed between waypoint
public float RangeDetection = 1.0f;                 //Range of detection between the npc and colliders
public AnimationClip[] WaypointWalkAnimationList;   //NPC walk animation list
public AnimationClip[] WaypointIdleAnimationList;   //NPC idle animation list

private Transform[] Waypoints;                      //Waypoint list depending on which sector (auto find the sector with the variable Sector =)
private Transform npc;
private WaypointState waypointState;
private int waypointIndex;                          //Waypoint index in the arrays of Waypoints
private float timeToWaitBeforeNextMove;             //Time to wait before changing waypoints
private Waypoint waypointReach;                     //Object referencing to script of the waypoint
private GameObject collide;
private float minTimeWait = 0.0f;                   //Minimum time to wait on waypoint depending of the waypoint (call MinimumTimeToWait() from Waypoint Script)
private float maxTimeWait = 0.0f;                   //Maximum time to wait on waypoint depending of the waypoint (call MaximumTimeToWait() from Waypoint Script)
private float defaultWalkSpeed;
private WaypointAnimator waitingOn;
RaycastHit hit;

void PlayRandomWalkAnimation()
{
    animation.CrossFade(WaypointWalkAnimationList[Random.Range(0, WaypointWalkAnimationList.GetLength(0))].name, 0.9f);
}

void PlayRandomIdleAnimation()
{
    animation.CrossFade(WaypointIdleAnimationList[Random.Range(0, WaypointIdleAnimationList.GetLength(0))].name, 0.2f);
}

void Start()
{
    GameObject[] temp = GameObject.FindGameObjectsWithTag("WaypointSector" + Sector);
    int lenght = temp.Length;
    Waypoints = new Transform[lenght];
    waypointIndex = StartupWaypointIndex;

    for (int i = 0; i < Waypoints.Length; i++)
    {
        Waypoints _= temp*.transform as Transform;*_

}
waypointState = WaypointState.MovingToNextWaypointPoint;
timeToWaitBeforeNextMove = -1.0f;
PlayRandomWalkAnimation();
transform.rigidbody.Sleep();
waypointReach = GetComponent(“Waypoint”) as Waypoint;
collide = this.gameObject;
defaultWalkSpeed = WaypointWalkSpeed;
}
void OnTriggerEnter(Collider col)
{
transform.rigidbody.Sleep();
collide = col.gameObject;
CheckIfWaypoint();
}
void OnCollisionEnter()
{
transform.rigidbody.Sleep();
}
void CheckIfWaypoint() //Check if the collider is a waypoint
{
if (collide == Waypoints[waypointIndex].gameObject)
{
waypointReach = collide.GetComponent(“Waypoint”) as Waypoint;
GetMinAndMaxTime();
}
}
void GetMinAndMaxTime() //Get the minimum and maximum time to wait on the current waypoint
{
minTimeWait = waypointReach.MinimumTimeToWait();
maxTimeWait = waypointReach.MaximumTimeToWait();
}
Vector3 GetCurrentDestination() //Get the position of the waypoint choose by the NPC
{
return Waypoints[waypointIndex].position;
}
void ChooseNextDestination() //If waypoint is available, choose new waypoint to reach
{
while (!IsAvailable())
{
waypointIndex = Random.Range(0, Waypoints.Length);
}
}
void UpdateMovingToNextWaypointPoint() //walk to the choosen waypoint and when waypoint reach, change state to idle till timeWait
{
Vector3 currentDestination = GetCurrentDestination();
transform.forward = currentDestination - transform.position;
transform.Translate(Vector3.forward * WaypointWalkSpeed * Time.deltaTime);
if ((GetCurrentDestination() - transform.position).magnitude < 0.1f)
{
timeToWaitBeforeNextMove = Random.Range(minTimeWait, maxTimeWait);
waypointState = WaypointState.WaitingForNextMove;
PlayRandomIdleAnimation();
}
}
void UpdateWaitingForNextMove() //While timeWait is reach, change state and choose another available waypoint
{
timeToWaitBeforeNextMove -= Time.deltaTime;
if (timeToWaitBeforeNextMove < 0.0f)
{
ChooseNextDestination();
waypointState = WaypointState.MovingToNextWaypointPoint;
PlayRandomWalkAnimation();
}
}
void Update()
{
if (waypointState == WaypointState.Evitate)
{
ChangeDirectionTemporary();
}
else if (waypointState == WaypointState.MovingToNextWaypointPoint)
{
WaypointWalkSpeed = defaultWalkSpeed;
PlayRandomWalkAnimation();
UpdateMovingToNextWaypointPoint();
ChangeAvailabilityOfWaypoint(false);
}
else if (waypointState == WaypointState.WaitingForNextMove)
{
WaypointWalkSpeed = defaultWalkSpeed;
UpdateWaitingForNextMove();
}
}
void LateUpdate()
{
if (rigidbody.SweepTest(transform.forward, out hit, RangeDetection)) //Check if NPC is colliding with another NPC
{
var other = hit.collider.gameObject.GetComponent(“WaypointAnimator”) as WaypointAnimator;
if (other && other.RecursivelyFind(this))
{
}
else
{
StartCoroutine(WaitForPass(1.5f)); //Only one of them change direction to not touch other NPC
waitingOn = other;
}
}
}
bool RecursivelyFind(WaypointAnimator x)
{
if (this == x)
{
return true;
}
if (!waitingOn)
{
return false;
}
return waitingOn.RecursivelyFind(x);
}
IEnumerator WaitForPass(float wait)
{
waypointState = WaypointState.Evitate;
yield return new WaitForSeconds(wait);
waypointState = WaypointState.MovingToNextWaypointPoint;
}
void ChangeDirectionTemporary() //Change direction while on way to collide with another NPC
{
transform.forward = GetCurrentDestination() - transform.localPosition + new Vector3(0, 0, 10f);
transform.Translate(Vector3.forward * WaypointWalkSpeed * Time.deltaTime);
}
void ChangeAvailabilityOfWaypoint(bool change) //Set choosen waypoint availability
{
waypointReach = Waypoints[waypointIndex].GetComponent(“Waypoint”) as Waypoint;
waypointReach.IsAvailable = change;
}
bool IsAvailable() //Check the waypoint availabilty
{
waypointReach = Waypoints[waypointIndex].GetComponent(“Waypoint”) as Waypoint;
if (waypointReach.IsAvailable)
return true;
else
return false;
}
}

I think there is any error in your proposed solution. You will get a deadlock not just when two AIs are facing each other, but whenever any set of AIs form a loop of waiting on each other. You need to actually check for such a loop. So, when an AI goes idle, it needs to record who it is waiting upon. If the AI it is waiting on is also waiting, then you need to check for a loop. Something like:

var waitingOn : WaypointAnimator;
...
if (SweepTest...) {
    var other = hit.gameObject.GetComponent.<WaypointAnimator>();
    if (other && other.RecursivelyFind(this)) {
        turn around NOW ....
    else
        waitingOn = other;
        ...
...
function RecursivelyFind(x : WaypointAnimator) : boolean
{
    if (this == x) return true;
    if (!waitingOn) return false;
    return waitingOn.RecursivelyFind(x);
}

Of course a two-AI loop is going to be most common, and that is the one you see so far, but this solution will work for that simple case too.