Skip to content

Instantly share code, notes, and snippets.

@jRayShumway00
Last active July 24, 2021 00:53
Show Gist options
  • Save jRayShumway00/8cac0d9e9e65753706fb1f1979f54bdd to your computer and use it in GitHub Desktop.
Save jRayShumway00/8cac0d9e9e65753706fb1f1979f54bdd to your computer and use it in GitHub Desktop.
Written as a tutorial to explain how to make an AI randomly navigate a NavMesh in Unity3D!
// We need UnityEngine and UnityEngine.AI to make this script work!
using UnityEngine;
using UnityEngine.AI;
/// <summary>
/// Welcome to the Random AI Naviation tutorial. I'm trying something different. In this
/// tutorial I will help you understand how to make an NavMeshAgent in Unity randomly
/// navigate the NavMesh. This is a basic script and you may expand upon it and use it
/// for free with no attribution required. Continue reading the comments for detailed
/// explanations for each line of code.
///
/// NOTE:
/// In the demo you can move around with the WASD keys and look around by holding the right
/// mouse button, then moving the mouse accordingly. The taller green line represents the AI's
/// current destination. The purple looking lines that are shorter, are there to represent all of
/// the other points that the AI might choose.
/// </summary>
public class AIController : MonoBehaviour
{
/// <summary>
/// Below are five lines of code, these are the variables required for our demo to work.
/// Two of these lines are only here to draw the lines in the demo representing the points
/// that the AI has chosen.
///
/// Let's look over the other lines of code here:
///
/// RANGE is used by this script to determine how far from the AI game object, that the AI can
/// choose a point from.
///
/// DESTINATION is used by this script to be a place to store the destination chosen for the AI.
///
/// AGENT is the NavMeshAgent that you want to have randomly navigate the NavMesh.
///
/// D1LR and D2LR can be removed as long as you remove the lines of code below (near the bottom) that
/// use them.
/// </summary>
public float range = 10.0f;
public Vector3 destination;
// You can remove the next line!
public LineRenderer d2lr;
NavMeshAgent agent;
// You can remove the next line!
LineRenderer d1lr;
/// <summary>
/// The below function or method determines the destination for the AI along with the other points which
/// (in the demo) are represented by the purple lines. Let's go over the code here so that you may understand
/// what is happening behind the scenes.
///
/// The method is of type BOOL because we want to use this later in an IF statement. We called this method RandomPoint
/// because it chooses a random point with the information given. The method takes in a Vector3 (which we call CENTER).
/// Center will later be set as the AI Root Object's Position. Next it takes in a RANGE FLOAT value. This float will be the
/// range of the selection sphere (the sphere used to limit the AI's available destination reach). Finally, the method sends
/// out a RESULT of type VECTOR3. This RESULT will be set to the AI's destination later.
///
/// Inside this method we have a basic For Loop, you can learn more about For Loops here: https://www.learncs.org/en/For_loops
/// inside the for loop we create a new Vector3 variable called RANDOMPOINT. And set that variable equal to the center (the AI's
/// transform position) plus the multiplication of a random point inside a unit sphere times the RANGE variable mentioned earlier.
/// You can read more about the Random.insideUnitSphere command here: https://docs.unity3d.com/ScriptReference/Random-insideUnitSphere.html
///
/// Next we create a NavMeshHit variable called HIT. We will use this later.
///
/// Following the hit var, we create an IF statement where we chech whether or not that the NavMesh.SamplePosition returns
/// true when given the following inputs:
///
/// randomPoint ( The Vector3 we created just a few seconds ago. )
/// out hit ( We want to send the value of this out to use later in this if statement. )
/// 1.0f ( The max distance for sampling. )
/// NavMesh.AllAreas ( The areaMask to sample from. )
///
/// If that returns true then the script will continue to the next commands. Which are:
///
/// result = hit.position;
///
/// And
///
/// return true;
///
/// The first command sets the value of result ( the variable we created in the method's parameter list ).
/// The last one there says that this method returned true and therefore has an output.
///
/// At the bottom of this method we have two more statements outside the IF statement. The first one sets result
/// to an empty Vector3 (contains only zeros for x, y, and z.) And this makes the method return false.
///
/// This conclude this section.
/// </summary>
bool RandomPoint(Vector3 center, float range, out Vector3 result)
{
for (int i = 0; i < 30; i++)
{
Vector3 randomPoint = center + Random.insideUnitSphere * range;
NavMeshHit hit;
if (NavMesh.SamplePosition(randomPoint, out hit, 1.0f, NavMesh.AllAreas))
{
result = hit.position;
return true;
}
}
result = Vector3.zero;
return false;
}
/// <summary>
/// The Start() method is called the first time that this script starts. Below we do a couple of things,
/// nothing complicated. Let's go over it.
///
/// First we set agent equal to the NavMeshAgent component that is attached to the GameObject that this
/// script is also attached to.
///
/// Then we set D1LR equal to the LineRenderer component attached to my AI in the Demo. You can either
/// comment that line out or remove that line from the code if you have removed the variable D1LR from
/// the variables list.
///
/// After that we set destination equal to agent.destination by default.
///
/// That's all.
/// </summary>
void Start()
{
agent = GetComponent<NavMeshAgent>();
// You can remove the next line!
d1lr = GetComponent<LineRenderer>();
destination = agent.destination;
}
/// <summary>
/// Update occurs once every frame, here we only have one command because we
/// create a new method to call for the rest of the logic here. This keeps
/// our code organized.
///
/// All we do here is call our (soon to be created) Patrol method. You can name
/// this whatever you want, but remember it because this is case sensitive like
/// most things in programming...
///
/// That's all for this method.
/// </summary>
void Update()
{
Patrol();
}
/// <summary>
/// Last but not least we have the Patrol method that we called earlier in Update.
/// You can name yours whatever you like, but I'm going to call it Patrol, just because.
///
/// In this method a few things happen, but let's go over them:
///
/// First we create a variable called point of type Vector3.
///
/// Next we create another IF statement. This if statement is checking for whether or not
/// the method RandomPoint returns true. Notice a few things different with this end of the
/// method. We are effectively calling the RandomPoint method and saying something like:
///
/// "Yo, so um I got this guy here this is his position and range, what point do you think he should
/// use?"
///
/// If the RandomPoint method returns true and all, it sends this if statement the value for RESULT which we named earlier.
/// The IF statement renames that variable for this section so we don't get them mixed up (it's why we named it point
/// here instead of result).
///
/// Then we do a couple of more things:
///
/// The first four lines in this IF statement are for the line renderers attached to the AI in the demo. You can removed them or
/// comment them out if you want.
///
/// But the import bit here is this nested IF statement. This one says, "Hey agent, are you on a path right now?" And then if
/// agent isn't on a path at the moment, the IF statement sets destination equal to the point var we renamed from RESULT and then
/// sends that to agent as the agent's new destination.
///
/// And that finishes the script. If those if statements return false, then it will do absolutely nothing.
/// </summary>
void Patrol()
{
Vector3 point;
if (RandomPoint(transform.position, range, out point))
{
// You can remove the next two lines!
d1lr.SetPosition(0, agent.destination);
d1lr.SetPosition(1, new Vector3(agent.destination.x, agent.destination.y + 5, agent.destination.z));
// You can remove the next two lines!
d2lr.SetPosition(0, point);
d2lr.SetPosition(1, new Vector3(point.x, point.y + 2.5f, point.z));
if (agent.hasPath == false) {
destination = point;
agent.destination = destination;
}
}
}
}
/* I hope this script was useful and taught you somethind about C#, Scripting, and AI in Unity. I discovered how to
* do this one day, while I was sitting at my computer. And figured I'd share it for free because I know back when I
* first got into Unity I wanted to know how to do this and I'm sure others do to. You can find a demo of this script
* in-use over on it's itch.io page here:
*
* https://deepdowngames.itch.io/unity-3d-ai-example
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment