Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Player jump not allowed on NavMeshAgent

Discussion in 'Scripting' started by ProtagonistKun, Jul 29, 2019.

  1. ProtagonistKun

    ProtagonistKun

    Joined:
    Nov 26, 2015
    Posts:
    352
    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?
     
  2. DanMarionette

    DanMarionette

    Joined:
    Jan 16, 2011
    Posts:
    27
    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?
     
  3. ProtagonistKun

    ProtagonistKun

    Joined:
    Nov 26, 2015
    Posts:
    352
    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.
     
    Last edited: Jul 29, 2019
  4. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,357
    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.
     
    ProtagonistKun likes this.
  5. ProtagonistKun

    ProtagonistKun

    Joined:
    Nov 26, 2015
    Posts:
    352
    In case my code helps find a solution in any way.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.AI;
    3.  
    4. namespace Utilities
    5. {
    6.     public class CharacterController : MonoBehaviour
    7.     {
    8.         [SerializeField, Header("Axis setup")] private string _horizontalAxis = "Horizontal";
    9.         [SerializeField] private string _verticalAxis = "Vertical";
    10.         [SerializeField] private string _jumpAxis = "Jump";
    11.  
    12.         private Vector2 moveForce;
    13.  
    14.         [SerializeField, Header("Move forces")] private float jumpForce = 10f;
    15.         [SerializeField] private float moveSpeed = 10f;
    16.  
    17.         private NavMeshAgent _agent;
    18.         private Rigidbody _rig;
    19.  
    20.         void Start()
    21.         {
    22.             _agent = GetComponent<NavMeshAgent>();
    23.             _rig = GetComponent<Rigidbody>();
    24.         }
    25.  
    26.         public void SetDestination(Vector3 destination)
    27.         {
    28.             _agent.destination = destination;
    29.         }
    30.  
    31.         private void StoreInput()
    32.         {
    33.             moveForce = new Vector2(Input.GetAxisRaw(_horizontalAxis), Input.GetAxisRaw(_verticalAxis));
    34.             moveForce *= moveSpeed;
    35.         }
    36.  
    37.         private void ExecuteMovement()
    38.         {
    39.             _rig.AddForce(moveForce.x, 0, moveForce.y);
    40.             if (Input.GetButtonDown(_jumpAxis))
    41.             {
    42.                 _rig.AddForce(0, jumpForce, 0, ForceMode.Impulse);
    43.             }
    44.         }
    45.  
    46.         public void Update()
    47.         {
    48.             StoreInput();
    49.             ExecuteMovement();
    50.         }
    51.     }
    52. }
     
  6. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,357
    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?
     
  7. ProtagonistKun

    ProtagonistKun

    Joined:
    Nov 26, 2015
    Posts:
    352
    The setDestination is called from a separate script, which is handling the mouseinput and converting the coordiantes
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. namespace Utilities
    4. {
    5.     public class MouseRay : MonoBehaviour
    6.     {
    7.         [SerializeField] private static string _groundTag = "Ground";
    8.  
    9.         private void Update()
    10.         {
    11.             if (Input.GetMouseButtonDown(0))
    12.             {
    13.                 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    14.                 if(Physics.Raycast(ray.origin, ray.direction, out RaycastHit rayCastHit, Mathf.Infinity))
    15.                 {
    16.                     //if it is an object with an OnClick function
    17.                     if (rayCastHit.transform.GetComponent<ClickableObject>())
    18.                     {
    19.                         rayCastHit.transform.GetComponent<ClickableObject>().Activate();
    20.                     }
    21.  
    22.                     if (rayCastHit.transform.CompareTag(_groundTag))
    23.                     {
    24.                         Manager.Instance.PlayerGameobject.GetComponent<CharacterController>().SetDestination(rayCastHit.point);
    25.                     }
    26.                 }
    27.             }
    28.         }
    29.     }
    30. }
     
  8. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,357
    Ok, I found the script that I mentioned earlier. It's pretty rough, though. It was originally for an enemy to seek the player.

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.AI;
    6.  
    7. public class NavSetTarget : MonoBehaviour {
    8.  
    9.  
    10.    NavMeshAgent _Agent;
    11.    Vector3[] _WayPoints =null;
    12.    int _NextWayPointIndex;
    13.    float _CloseToWayPointRange=1f;
    14.    float _WalkSpeed=0.3f;
    15.    public raycastObstacleAvoidance _ROA=null;
    16.    rigidbodyCharacterController _RCC;
    17.  
    18.    // Use this for initialization
    19.    void Start () {
    20.        _Agent = GetComponent<NavMeshAgent> ();
    21.        _Agent.updatePosition = false;
    22.        _Agent.updateRotation = false;
    23.        _RCC = GetComponent<rigidbodyCharacterController>();
    24.  
    25.        InvokeRepeating ("getNewPath", 3, 1f);
    26.  
    27.    }
    28.  
    29.    void OnDrawGizmos ()
    30.    {
    31.        if (_WayPoints != null)
    32.        for (int i = 0; i < _WayPoints.Length; i++) {
    33.                Gizmos.color = Color.green;
    34.            Gizmos.DrawSphere (_WayPoints [i], 0.5f);
    35.        }
    36.    }
    37.    // Update is called once per frame
    38.    void Update () {  
    39.  
    40.        if (_WayPoints==null) {
    41.            return;
    42.        }
    43.  
    44.         // find angle from the character to the next way point
    45.        Vector3 v3Dir =  _WayPoints[_NextWayPointIndex]-transform.position;
    46.        float angle = Vector3.Angle(transform.forward,v3Dir);
    47.        _RCC.Rotate(0,angle,0);
    48.  
    49.        //Walk to the way point until it's in range. Then go on to the next way point
    50.        if (Vector3.Distance (_WayPoints [_NextWayPointIndex], transform.position) < _CloseToWayPointRange) {
    51.            _NextWayPointIndex += 1;
    52.        }  
    53.        if (_NextWayPointIndex > _WayPoints.Length - 1) {
    54.            getNewPath ();
    55.        }
    56.  
    57.  
    58.        _RCC.Velocity=Vector3.forward * _WalkSpeed;
    59.        
    60.    }
    61.  
    62.  
    63.    void getNewPath()
    64.    {   _Agent.nextPosition = transform.position;
    65.        _Agent.SetDestination (GameObject.FindGameObjectWithTag ("Player").transform.position);
    66.        _WayPoints = _Agent.path.corners;
    67.        _NextWayPointIndex = 0;
    68.    }
    69. }
    70.  
    71.  
    72.  
    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.
     
    spiritworld and ProtagonistKun like this.
  9. spiritworld

    spiritworld

    Joined:
    Nov 26, 2014
    Posts:
    29
    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.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.AI;
    4.  
    5. public class NpcController: MonoBehaviour {
    6.        NavMeshAgent agent;
    7.         void OnEnable()
    8.         {
    9.             agent.updatePosition = true;
    10.             agent.updateRotation = true;
    11.             agent.isStopped = false;
    12.         }
    13.  
    14.         void OnDisable()
    15.         {
    16.             agent.updatePosition = false;
    17.             agent.updateRotation = false;
    18.             agent.isStopped = true;
    19.         }
    20.        
    21.         /// NPC CONTROLLING STUFF HERE
    22. }
    23.