Player jump not allowed on NavMeshAgent

Hello,

I have a problem when combining a navmesh agent with a rigidbody.

Whenever I try to jump nothing happens, but as soon as I disable the NavMeshAgent it works.
I have found out that the physics are being overridden by the NavMeshAgent and thus jumping cant be performed.

My question is, how do I get around that, I can move the rigidbody to the mesh child but since I use forces to move my player, this leaves the parent object unable to move. As it then only responds to the navmesh. I have tried this and this jump works, but than the movement is weird. All i can think to do is have 2 rigidbodies, one on the base object and one on the mesh where the mesh is responsible for jumping (and holds the collider) and its parent is taking care of the movement… But I cant think of a better way of doing it. So I head to the forums to get some other peoples insights on my problem.

TL;DR: Does anyone know how I can make the navmesh agent stop blocking my rigidbody from creating a jump impulse (without having to move the navmesh and rigidbody to separate objects) while both the navmesh and axis inputs work for the movement?

As far as I am aware, the NavMeshAgent sets the objects position on all 3 axis. This would enable the agent to walk up stairs/ramps. My first thought was to use separate objects, but you say you don’t want that. Any reason why? Does that create another problem for you?

Well I am doing my movement with rigid body impulses. Problem is if I put the rigidbody on the navmeshagent I can’t jump but can move. If I put it on a separate object I can jump and move but than using a different movement method (raycast and navigate with mouse click) would act weird since its a different object.

Edit: By weird I mean that the parent of the player mesh would remain stationary if you use the Axis system to move. than by using raycast and navmesh destination it would be offsetted and not be the desired result. I hope this is clear.

I’ve moved an object with a rigidbody and a navmesh agent successfully before. I bypassed that navmesh agent’s movement and used it strictly to generate a path (CalculatePath). Then I retrieved the path as an array of points (path.corners). I used the rigidbody to move from one point to the next. I’ll see if I can find the code when I get home.

1 Like

In case my code helps find a solution in any way.

using UnityEngine;
using UnityEngine.AI;

namespace Utilities
{
    public class CharacterController : MonoBehaviour
    {
        [SerializeField, Header("Axis setup")] private string _horizontalAxis = "Horizontal";
        [SerializeField] private string _verticalAxis = "Vertical";
        [SerializeField] private string _jumpAxis = "Jump";

        private Vector2 moveForce;

        [SerializeField, Header("Move forces")] private float jumpForce = 10f;
        [SerializeField] private float moveSpeed = 10f;

        private NavMeshAgent _agent;
        private Rigidbody _rig;

        void Start()
        {
            _agent = GetComponent<NavMeshAgent>();
            _rig = GetComponent<Rigidbody>();
        }

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

        private void StoreInput()
        {
            moveForce = new Vector2(Input.GetAxisRaw(_horizontalAxis), Input.GetAxisRaw(_verticalAxis));
            moveForce *= moveSpeed;
        }

        private void ExecuteMovement()
        {
            _rig.AddForce(moveForce.x, 0, moveForce.y);
            if (Input.GetButtonDown(_jumpAxis))
            {
                _rig.AddForce(0, jumpForce, 0, ForceMode.Impulse);
            }
        }

        public void Update()
        {
            StoreInput();
            ExecuteMovement();
        }
    }
}

I don’t quite understand what you’re doing there? If you are moving using AddForce with moveForce, what do you need the navmesh agent for? When does SetDestination ever get called?

The setDestination is called from a separate script, which is handling the mouseinput and converting the coordiantes

using UnityEngine;

namespace Utilities
{
    public class MouseRay : MonoBehaviour
    {
        [SerializeField] private static string _groundTag = "Ground";

        private void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                if(Physics.Raycast(ray.origin, ray.direction, out RaycastHit rayCastHit, Mathf.Infinity))
                {
                    //if it is an object with an OnClick function
                    if (rayCastHit.transform.GetComponent<ClickableObject>())
                    {
                        rayCastHit.transform.GetComponent<ClickableObject>().Activate();
                    }

                    if (rayCastHit.transform.CompareTag(_groundTag))
                    {
                        Manager.Instance.PlayerGameobject.GetComponent<CharacterController>().SetDestination(rayCastHit.point);
                    }
                }
            }
        }
    }
}

Ok, I found the script that I mentioned earlier. It’s pretty rough, though. It was originally for an enemy to seek the player.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class NavSetTarget : MonoBehaviour {


   NavMeshAgent _Agent;
   Vector3[] _WayPoints =null;
   int _NextWayPointIndex;
   float _CloseToWayPointRange=1f;
   float _WalkSpeed=0.3f;
   public raycastObstacleAvoidance _ROA=null;
   rigidbodyCharacterController _RCC;

   // Use this for initialization
   void Start () {
       _Agent = GetComponent<NavMeshAgent> ();
       _Agent.updatePosition = false;
       _Agent.updateRotation = false;
       _RCC = GetComponent<rigidbodyCharacterController>();

       InvokeRepeating ("getNewPath", 3, 1f);

   }

   void OnDrawGizmos ()
   {
       if (_WayPoints != null)
       for (int i = 0; i < _WayPoints.Length; i++) {
               Gizmos.color = Color.green;
           Gizmos.DrawSphere (_WayPoints [i], 0.5f);
       }
   }
   // Update is called once per frame
   void Update () {   

       if (_WayPoints==null) {
           return;
       }

        // find angle from the character to the next way point
       Vector3 v3Dir =  _WayPoints[_NextWayPointIndex]-transform.position;
       float angle = Vector3.Angle(transform.forward,v3Dir);
       _RCC.Rotate(0,angle,0);

       //Walk to the way point until it's in range. Then go on to the next way point
       if (Vector3.Distance (_WayPoints [_NextWayPointIndex], transform.position) < _CloseToWayPointRange) {
           _NextWayPointIndex += 1;
       }   
       if (_NextWayPointIndex > _WayPoints.Length - 1) {
           getNewPath ();
       }


       _RCC.Velocity=Vector3.forward * _WalkSpeed;
       
   }


   void getNewPath()
   {   _Agent.nextPosition = transform.position;
       _Agent.SetDestination (GameObject.FindGameObjectWithTag ("Player").transform.position);
       _WayPoints = _Agent.path.corners;
       _NextWayPointIndex = 0;
   }
}

I hope you get the idea, though. In Start, I explicitly set the agent updatePosition and UpdateRotation to false so that I can use my RigidBody Character Controller to move the character instead. I use the NavMesh agent only to compute the path to the target and then I incrementally move and rotate towards the next way point using the rigidbody.

2 Likes

Oh, thanks! This was just what I needed. I had overlooked that updatePosition field. My player character has navmeshagent so other agents understand to avoid him and I can push them too, additionally I can activate AI mode. Unfortunately nothing is so simple and next I realize I can’t jump! Instead Player crashes thru the floor into void.

I have NpcController.cs which I externally switch on/off when I want to switch between manual/AI controlled schemes.

using UnityEngine;
using UnityEngine.AI;

public class NpcController: MonoBehaviour {
       NavMeshAgent agent;
        void OnEnable()
        {
            agent.updatePosition = true;
            agent.updateRotation = true;
            agent.isStopped = false;
        }

        void OnDisable()
        {
            agent.updatePosition = false;
            agent.updateRotation = false;
            agent.isStopped = true;
        }
        
        /// NPC CONTROLLING STUFF HERE
}