Last active
July 24, 2021 00:53
-
-
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!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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