Search code examples
unity-game-enginenavmesh

Unity - How to jump using a NavMeshAgent and click to move logic


I am building a game where the player can be controlled using the mouse input, using a click to move logic via a navmesh agent.

In order to let the player jump, I started using a CharacterController as well which should help managing the player. My issue is that I can't figure out where to put the jump logic. All references I found are related using the character controller without the navmesh agent.

I can get rid of the CharacterController if needed, but the NavMeshAgent has to stay.

Here it is a working code which allows to walk. Can you please help me with the jumping logic?

  private NavMeshAgent _agent;
  private CharacterController _characterController;
  private Vector3 _desVelocity;

  private void Update()
  {
     if (Input.GetMouseButtonDown(0))
     {
         Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
         if (Physics.Raycast(ray.origin, ray.direction, out RaycastHit hitInfo))
         {
             _agent.destination = hitInfo.point;
         }
     }
     var currMovementDirection = _desVelocity.normalized * currentSpeed;
     if (_agent.remainingDistance > _agent.stoppingDistance)
     {
         _desVelocity = _agent.desiredVelocity;
         _characterController.Move(currMovementDirection * Time.deltaTime);
     }
 }

Solution

  • You can achieve this using a Rigidbody instead of a CharacterController. The trick is that you need to disable the NavMeshAgent in order to jump.

    Optionally, you set the destination to where you are at the time of the jump, so that the agent doesn't continue the simulation while the jump is happening.

    Using collision detection, you turn the NavMeshAgent back on again once you land.

    public class PlayerMovement : MonoBehaviour
    {
    
        private Camera cam;
        private NavMeshAgent agent;
        private Rigidbody rigidbody;
        public bool grounded = true;
    
        void Start()
        {
            cam = Camera.main;
            agent = GetComponent<NavMeshAgent>();
            rigidbody = GetComponent<Rigidbody>();
        }
    
        void Update()
        {
            // clicking on the nav mesh, sets the destination of the agent and off he goes
            if (Input.GetMouseButtonDown(0) && (!agent.isStopped))
            {
                Ray ray = cam.ScreenPointToRay(Input.mousePosition);
                if (Physics.Raycast(ray, out RaycastHit hit))
                {
                    agent.SetDestination(hit.point);
                }
            }
    
            // when you want to jump
            if (Input.GetKeyDown(KeyCode.Space) && grounded)
            {
                grounded = false;
                if (agent.enabled)
                {
                    // set the agents target to where you are before the jump
                    // this stops her before she jumps. Alternatively, you could
                    // cache this value, and set it again once the jump is complete
                    // to continue the original move
                    agent.SetDestination(transform.position);
                    // disable the agent
                    agent.updatePosition = false;
                    agent.updateRotation = false;
                    agent.isStopped = true;
                }
                // make the jump
                rigidbody.isKinematic = false;
                rigidbody.useGravity = true;
                rigidbody.AddRelativeForce(new Vector3(0, 5f, 0), ForceMode.Impulse);
            }
        }
    
        /// <summary>
        /// Check for collision back to the ground, and re-enable the NavMeshAgent
        /// </summary>
        private void OnCollisionEnter(Collision collision)
        {
            if (collision.collider != null && collision.collider.tag == "Ground")
            {
                if (!grounded)
                {
                    if (agent.enabled)
                    {
                        agent.updatePosition = true;
                        agent.updateRotation = true;
                        agent.isStopped = false;
                    }
                    rigidbody.isKinematic = true;
                    rigidbody.useGravity = false;
                    grounded = true;
                }
            }
        }
    }