bre_dev
bre_dev

Reputation: 572

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);
     }
 }

Upvotes: 1

Views: 9890

Answers (2)

Dover8
Dover8

Reputation: 595

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;
            }
        }
    }
}

Upvotes: 2

Saad Anees
Saad Anees

Reputation: 1430

The jump logic should be inside the Update() method since we want the height to be calculated every frame.

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;

    groundedPlayer = _characterController.isGrounded;
    if (groundedPlayer && currMovementDirection.y < 0)
    {
        currMovementDirection.y = 0f;
    }



    // Changes the height position of the player..
    if (Input.GetButtonDown("Jump") && groundedPlayer)
    {
        currMovementDirection.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
    }

    currMovementDirection.y += gravityValue * Time.deltaTime;
    if (_agent.remainingDistance > _agent.stoppingDistance)
    {
        _desVelocity = _agent.desiredVelocity;
        _characterController.Move(currMovementDirection * Time.deltaTime);
    }
}

Please see the official docs here

Upvotes: 1

Related Questions