Random NPC Movement & Stopping Within A Specific Distance

I'd like my NPC characters to move and stop randomly within a certain range.

Here's an example of how I'd like the orange NPC to be able to move and stop freely within the green box:

Is there a simple way to achieve this? Thanks!

• How are you moving your NPCs at present? Are they using NavmeshAgent components, or direct transform.position setting, or Rigidbody(2D).velocity, or something else? Presumably you've looked at functions to generate random points within a rectangular region as destinations for the NPCs? Commented Feb 11, 2023 at 22:03
• I did try searching, but your search query looks much better! How can I specify a rectangular region to constrain them? Currently I'm just setting their transform.position directly. I'm not familiar with NavmeshAgents. Do you recommend I move them that way? Commented Feb 11, 2023 at 22:22
• "How can I specify a rectangular region to constrain them?" - that seems to be covered in the search hits I linked, no? You'd use that algorithm to choose a destination point in the rectangle, then either dead-reckon toward it with tools like MoveTowards or pathfind to it using a navmesh if the NPCs need to avoid obstacles. Being convex, any direct path between two points in a rectangle is also in that rectangle, so you shouldn't need other constraints unless some effect can push agents outside, or an obstacle forces them to go around. Commented Feb 11, 2023 at 22:31
• Sorry, I should have been more specific. Obtaining the random point makes sense to me, but I'm not quite understanding where the rectangle comes from. My NPCs are currently placed on a 3D cube. How can I create this rectangle to find points in? Commented Feb 12, 2023 at 0:35
• The link I gave just asks for a minimum and maximum x and z coordinate (or equivalently, a center x/z and a width/depth). Those 4 numbers are enough to define a rectangle in world space. But maybe I'm not following what you mean? Want to try over Discord if a more informal chat would help? Commented Feb 12, 2023 at 0:46

DMGregory connected with me on Discord and clarified how this algorithm works.

To elaborate-

This algorithm creates a rectangle by defining the corners with min/max. The arguments to Random.Range define the corners of the rectangle, and the return value gives you one point randomly chosen between those corners.

So as an example, my game is 3D. So new Vector3(Random.Range(minX, maxX), 0, Random.Range(minZ, maxZ)) gives a vector3 with random points between the corners (the min/max) of both the x-axis & the z-axis.

But he also went a step further and provided me with this extremely helpful script that draws a rectangle in the scene. This is great for visualizing where my NPC will be able to walk to. You can simply position the object in your scene and call GetRandomPoint().

public class RectangleZone : MonoBehaviour {
public float width = 10f;
public float depth = 10f;

public Vector3 GetRandomPoint() {
var local = new Vector3(
(Random.value - 0.5f) * width,
0,
(Random.value - 0.5f) * depth);

return transform.TransformPoint(local);
}

void OnDrawGizmos() {
Gizmos.color = Color.cyan;
var cachedMatrix = Gizmos.matrix;
Gizmos.matrix = transform.localToWorldMatrix;

Gizmos.DrawWireCube(Vector3.zero, new Vector3(width, 0, depth));

Gizmos.matrix = cachedMatrix;
}
}


In the first method you can simply create a vector3 and use Random.Range twice like so:

Vector3 randomPoint = new Vector3( Random.Range(0f, 10f),
0,
Random.Range(0f, 10f) );


If you'd like to incorporate the second method that creates a visualized rectangle in your scene, you simply call the GetRandomPoint() function and it will return the new vector3:

Vector3 randomPoint = GetRandomPoint();


Whether you use the former or the latter, once you have the vector3 stored in a variable, you can use your preferred method of moving a gameObject to the new location. This can be done in update/coroutine with a for/while loop moving with MoveTowards,Lerp,etc... The implementation is flexible and this is is just one example:

while (transform.position != _targetLocation)
{
var step = _npcWalkSpeed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, _targetLocation, step);
yield return null;
}


Hope this helps someone else!