Accessing NavMeshPath, or script to modify path.

Hello all!

Ok, so… Lets say I have 20 agents, and they are all going to the same place. It works great but they all walk in single-file… So, is there any way I can modify the path during run-time so that the path will be a little bit more randomized…

An example: I’m working on a zombie survival, and the zombies can’t all be walking single-file to the player… so I need some way to get access to the NavMeshPath variables to write my own script or find a script that will take a pre-calculated path, and randomize it a little bit, make them stray from the path a little bit, if they are in an open area to spread out a little bit. Things like that :slight_smile:

thanks,
why789

bump

bump, isn’t anyone else bothered by this?

why don’t you slightly change each zombie’s velocity, make it random, and you’ll see it looks pretty nice.

In a crowd if everybody moves at the same speed to the same point, easiest way is straight line.

it does make a difference however, they still all walk in a straight line from point A to point B. Is there anyway for them to swerve (I don’t know the correct word). As in, get from point A to point B but in randomized S curves?

Thanks,
why789

Maybe you shouldn’t give all 20 agents the exact same destination. Give the agents smarter destination points that aren’t directly toward the player, but are still in the general direction.

How would I go about doing that? Theories??

Basically check for an open space around player and put destination to that location?

Maybe using a randomized position relative to the target and making the random upper limit depend on the distance to target, so when i get close the random is lower…
something like

float randomOffset = Random.Range(0, Vector3.distance(transform.position, target.position)/10);
vector3 offsetVector = new Vector3(randomOffset, 0, randomOffset);
navMeshAgent.destination = target.position+offsetVector;

or maybe if you want it to zig zag you could make the offset a sine function and perpendicular to the agent-target vector

maybe like

float randomOffset = Mathf.Sin(Time.time*5)Vector3.distance(transform.position, target.position)/10;
navMeshAgent.destination = target.position+transform.right
randomOffset;

use and change the “5” to control frequency
use and change the “10” to control distance/offset ratio

haven’t tried anything written above. hope it works

float randomOffset = Random.Range(0, Vector3.distance(transform.position, target.position)/10);
vector3 offsetVector = new Vector3(randomOffset, 0, randomOffset);
navMeshAgent.destination = target.position+offsetVector;

worked the best however still not quite what I’m looking for, you see, it seems like the path is calculated based on corners, so they all go towards the same corner and only when they are on the last stretch towards the player do they actually spread out due to the randomness…

Anyway to do this so they spread out on the whole journey towards the target? In the NavMeshPath class there is a corners vector3 array but it is read only, if I set up my own navigation array and i Lerp the current position to the next corner (plus a bit of randomization) than that could work but I’m not sure how this would work performance wise as well as if it’s even possible (and if I know how to do it O.o) anyone have any opinions on that?

well… i think you will eventually have to use NavMesh.CalculatePath… (NavMesh with capital N)

What i would do is Calculate a path to my target, something like this:

NavMeshPath tempPath;
NavMesh.CalculatePath(transform.position, target.position, -1, tempPath);

that gives you a NavMeshPath ( tempPath), now from that path you check if it has more than one corner (cause it could be null )and if it does then make the temporary target’s position equal to that first corner (which is a vector3) +the offset we already checked above. that should definately give you the zigzagging + avoidance…

or something like that…

NavMesh refers tho THE navmesh ( the one you baked on the scene ), do not declare a NavMesh type variable, you dont need to.(or at least didn’t work for me)

gotcha, I’ll post a reply based on my results…

Question: but if i am randomizing a little bit on the corners what if I set my destination inside something not walkable?
Is there any way to make sure the randomization doesn’t end up with a position inside something unwalkable?

EDIT: just tried it out, the way im doing it is not viable at all due to cpu usage…
I have to have the agents recalculate their destination every second.
At the moment I get the corner vector3 and than add in a bit of randomization. than I use the SetDestination function to set the destination to the randomized corner vector. Once I arrive to that corner I use the SetDestination to the next corner. CalculatePath is too expensive for over 100 agents being called every second.

Without the randomization I could get 70-80 FPS constant recalculating path every second for 130 agents. Now I get around 70FPS and than spikes down to 40FPS (the times when CalculatePath is called on all agents)

thanks,
why789

thanks,
why789

shoot, that’s a lotta agents

If I recall (and I just did) you started with “Lets say I have 20 agents”… Liar!!!

Are your agents primitives? or animated shaded models?
I mean is pathfinding the only thing left to work out in your scene or the 40 fps refer to just primitives chasing primitives?

primitives chasing primitives, obviously once animation is underway there won’t be 130 agents running around. These 130 agents are just in a stress test scene (a maze)

there won’t be 130 agents runtime :)… I don’t think…

Well then you can use some kind of control for the number of agents. like if they are too far behind the player they wont chase him anymore. Or at least not using pathfinding… a simple scripted zombie AI for Offscreen zombies if you will… you cant see them anyway

well the only time pathfinding will be used is when zombies are chasing you. everything else wont be pathfinding perse

and the zombies need to chase you even if you aren’t looking at them…

any ideas people?
Basically I just need a zombie to find a path to you. Since you are moving it needs to be checked relatively often. any way to do this as well as add a little bit of randomization?

I have yet to play around with the NavMesh so I don’t know if this is possible or realistic. Can you create waypoints along the path so your zombies are traveling small distances before recalculating the player position? Then you can add your randomization to the waypoint area so everybody is not traveling single file like they are now.

That’s what MarioRuiz suggested (with the corners) however it’s not feasible due to performance, seems like it’s faster too calculate 1 long path than 10 straight ones…

Anyway I solved the walking in single file problem by adding a simple randomized sine wave to their movement using the Move function in the NavMeshAgent.

This still however is a pretty big problem if you want to do more than basic randomization with the calculated paths.

Thank you all!
why789

For anyone looking here for example, I have implented this using Perlin noise:

  public class ChasingBehaviour
    : MonoBehaviour
  {
    [SerializeField] private float _rate = 1;
    [SerializeField] private float _amplitude = 3;
  
    private NavMeshAgent _agent;
    private float _baseSpeed;
    private float _perlinTime;

    private void Awake()
    {
      _agent = GetComponent<NavMeshAgent>();
      _agent.updateRotation = false;
      _agent.updateUpAxis = false;
    }

    private void OnEnable()
    {
      _baseSpeed = _agent.speed;
      _perlinTime = Random.Range(0f, 1000f);
    }

    private void Update()
    {
      _perlinTime += Time.deltaTime * _rate;
      var moveDirection = Vector2.Perpendicular(_agent.velocity).normalized;
      var moveVelocity = moveDirection * Mathf.Lerp(-1f, 1f, Mathf.PerlinNoise(_perlinTime, 0f)) * _amplitude;
      _agent.Move(moveVelocity * Time.deltaTime);
      _agent.speed = _baseSpeed - moveVelocity.magnitude;
    }

    public void SetDestination(Vector3 destination)
    {
      _agent.destination = destination;
    }
  }

This is a 2D version. For a 3D variant you would want to remove _agent.updateRotation = false; _agent.updateUpAxis = false; and calculate moveDirection in some 3D way.
An example of fun setup:
Agent Speed : 15
Rate : 2
Amplitude : 10