0
\$\begingroup\$

Alrighty, so I've seen a lot of stuttering-related questions about NavMeshAgents but my problem is a little specific to my use case since it has nothing to do with NavMesh itself as a system. The system works great for me, it's simply a problem with how I am implementing it. I'm sure there is a solution, I'm just not sure what it is.

So, onto the actual issue. I am using NavMesh and NavMeshAgents to facilitate a mouse-click style movement system (think Diablo or Path of Exile). When the mouse is held down, the player will constantly move towards that location using a Raycast from the camera to the NavMesh to get and set the destination.

However, despite using the stoppingDistance variable on NavMeshAgents, it doesn't matter because if the player holds down the mouse button near the threshold of the stopping distance, the character will constantly switch states between destination reached to new destination found.

My code is a bit messy, so apologies for that, this is a quickly thrown together prototype of my movement system. That being said, I don't actually think my code is the problem, I think this is simply a very niche problem I don't know how to solve.

using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;

[RequireComponent(typeof(NavMeshAgent))]
public class PlayerController : MonoBehaviour
{
    [SerializeField] private LayerMask navigableMask;
    
    private NavMeshAgent _agent;
    private Camera _followCamera;

    private void Start()
    {
        _followCamera = Camera.main;
        _agent = GetComponent<NavMeshAgent>();
    }

    private void Update()
    {
        HandleMovement();
    }

    private void OnEnable()
    {
        movementInput.Enable();
    }

    private void OnDisable()
    {
        movementInput.Disable();
    }

    private void HandleMovement()
    {
        if (movementInput.ReadValue<float>() == 1f)
        {
            Ray ray = _followCamera.ScreenPointToRay(Mouse.current.position.ReadValue());

            if (Physics.Raycast(ray, out var hit, 100, navigableMask))
            {
                Move(hit.point);
            }
        }
    }

    private void Move(Vector3 destination)
    {
        _agent.SetDestination(destination);
    }
}

Here is a gif of the problem, in action.

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

This looks like a problem you could solve with a bit of hysteresis.

While the mouse button is held:

  • If the character is already in the current stopping distance, change the stopping distance to a "large" distance (so that they don't start moving from a small mouse move - they'll let it get a little farther away before chasing).

  • If the character is not already in the current stopping distance, change the stopping distance to a "small" distance (so that they have some room to move without stopping short after only half a step).

This can still create some stop-and-start, but by tuning the sizes of the large and small distances, you can change it from jerky hesitation to a more deliberate-looking pause & run to catch up.

\$\endgroup\$
3
  • \$\begingroup\$ I'll give this a try and mark this as solved if it works. This is the exact type of answer I was hoping for! \$\endgroup\$ Apr 16, 2023 at 22:07
  • 1
    \$\begingroup\$ Marked as solution! Worked like a charm and was extremely easy to implement. Thanks a lot! \$\endgroup\$ Apr 17, 2023 at 0:57
  • \$\begingroup\$ Glad to hear it! This kind of hysteresis tends to work well for the whole family of problems of "flickery/jittery inconsistent behaviour near a trigger threshold", so it's a good general-purpose tool to have in your kit. 😁 \$\endgroup\$
    – DMGregory
    Apr 17, 2023 at 2:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .