Search Unity

NavMeshAgent and Physics with Rigidbody

Discussion in 'Physics' started by clamum, Feb 28, 2020.

  1. clamum

    clamum

    Joined:
    May 14, 2017
    Posts:
    61
    So I have a bunch of people objects in my game, walking around the game board using the NavMesh components (random points are chosen throughout the game board and the people objects walk to those destinations using the NavMesh).

    I also have another object that can fly around and hit the people, and when this object his a person I want that person to kind of go flying a bit, in a ragdoll sort of way.

    I thought this would be simple with Unity physics but I cannot get this to happen.

    I did a bit of searching and it seems there's some considerations when using NavMeshAgent and Rigidbody/physics: https://docs.unity3d.com/Manual/nav-MixingComponents.html

    So I tried just turning "isKinematic" to TRUE on my people prefabs but that didn't do anything.

    Does anyone have any ideas of what's wrong? I've included my relevant code below.

    Person.cs
    Code (CSharp):
    1.  
    2.     public NavMeshAgent NavMeshAgent;
    3.     public Vector3 CurrentDestination;
    4.     public Rigidbody rigidbody;
    5.  
    6.     private void Start()
    7.     {
    8.         path = new NavMeshPath();
    9.         NavMeshAgent = GetComponent<NavMeshAgent>();
    10.         rigidbody = GetComponent<Rigidbody>();
    11.  
    12.         // move to random location on the game board
    13.         if (CurrentDestination != null)
    14.         {
    15.             bool pathFound = NavMeshAgent.CalculatePath(CurrentDestination, path);
    16.  
    17.             //Debug.Log("Path found: " + pathFound);
    18.  
    19.            NavMeshAgent.SetPath(path);
    20.         }
    21.  
    22.         //Debug.Log("SetDestination for " + name);
    23.     }
    24.  
    25. void OnCollisionEnter(Collision collision)
    26.     {
    27.         if (collision.gameObject.tag == "Ground" || collision.gameObject.tag == "Building" || collision.gameObject.tag == "Vehicle")
    28.         {
    29.             //Debug.Log(string.Format("Hit {0}", gameObject.tag));
    30.  
    31.             if (!hasPlopped)
    32.             {
    33.                 hasPlopped = true;
    34.  
    35.                  // unimportant; just resets the flying object
    36.                 StartCoroutine(DoAPlop(2));
    37.             }
    38.         }
    39.         else if (collision.gameObject.tag == "Person")
    40.         {
    41.             if (!hasPlopped)
    42.             {
    43.                 hasPlopped = true;
    44.                 hasPersonCollisionOccurred = true;
    45.  
    46.                 collisionPerson = collision.gameObject.GetComponent<Person>();
    47.  
    48.                 AudioSource audio = collision.gameObject.AddComponent<AudioSource>();
    49.                 audio.PlayOneShot((AudioClip)Resources.Load("Explosion"));
    50.  
    51.                  // unimportant; just resets the flying object
    52.                 StartCoroutine(DoAPlop(4));
    53.             }
    54.         }
    55.     }
    56.  
    Plops.cs (this is the object that falls from the sky and hits the person)
    Code (CSharp):
    1.  
    2. private void FixedUpdate()
    3.     {
    4.         if (!hasPlopped)
    5.         {
    6.             Vector3 forward = Camera.main.transform.forward;
    7.             forward.y = 0;
    8.  
    9.             // these velocity calcs and the rigidbody.velocity assignment below are for the
    10.             // player to control the falling object by swiping
    11.             // i don't think this is a problem
    12.             Vector2 currentVelocity = new Vector2(rigidbody.velocity.x, rigidbody.velocity.z);
    13.             Vector2 targetVelocity = Vector2.MoveTowards(currentVelocity, RotateVector(ScreenSpaceController.Instance.GetVector(), Vector3.SignedAngle(forward, Vector3.forward, Vector3.up)) * MaxHorizontalVelocity, MaxAcceleration * Time.deltaTime);
    14.  
    15.             rigidbody.velocity = new Vector3(targetVelocity.x, rigidbody.velocity.y, targetVelocity.y);
    16.         }
    17.  
    18.         // a collision with a person has occurred
    19.         if (hasPersonCollisionOccurred)
    20.         {
    21.             if (rigidbody != null && collisionPerson != null)
    22.             {
    23.                 Debug.Log("Person collision");
    24.  
    25.                 collisionPerson.NavMeshAgent.enabled = false;
    26.                 collisionPerson.rigidbody.isKinematic = false;
    27.  
    28.                 collisionPerson.rigidbody.AddForce(0, 0, 10000, ForceMode.Impulse);
    29.  
    30.                 collisionPerson.NavMeshAgent.velocity = collisionPerson.rigidbody.velocity;
    31.  
    32.                 hasPersonCollisionOccurred = false;
    33.  
    34.                 collisionPerson.NavMeshAgent.enabled = true;
    35.                 collisionPerson.rigidbody.isKinematic = true;
    36.             }
    37.             else
    38.             {
    39.                 Debug.Log("Person rigidbody is NULL");
    40.             }
    41.         }
    42.     }
    43.  
    44.  
     
    Last edited: Mar 11, 2020
  2. clamum

    clamum

    Joined:
    May 14, 2017
    Posts:
    61
    Bump. :(

    I've corrected a couple errors in my description and updated the code. You now see the NavMesh part of the Person class (CalculatePath and SetPath) as well as the "FixedUpdate" method in the falling object that hits the Person (called Plop).

    As you can see, I toggle off the NavMeshAgent and the Person's isKinematic flag before trying to add force to their rigidbody, but it seems to do nothing. I even set my falling object to have a mass of 100 and the people to have a mass of 0.1.

    One thing to note, just before this I was mistakenly assigning the "addForce()" call to the falling object's rigidbody, instead of the person's. This caused the falling object to go flying to the side when the collision occurred. So the physics does work, but the people just do not move according to my "addForce()" physics call, I'm sure because of the NavMeshAgent component but since I'm toggling it off, I don't know what else to do.
     
  3. Cookieg82

    Cookieg82

    Joined:
    Mar 28, 2017
    Posts:
    73
    I am in exactly the same position... NavMesh with Rigidbody and the Gravity and Kinematics flags are true. As directed by Unity. However, I would like to apply some force to the ragdoll or even just get ragdoll to work without conflicting with the NavMesh and the agent settings... stuck!?
     
    Alex_Hosseini likes this.
  4. clamum

    clamum

    Joined:
    May 14, 2017
    Posts:
    61
    You probably have already moved on, but I did get this working.

    Are you still stuck? I could share my code. It's super late right now and I'm going to bed but if you want I can update this tomorrow.
     
  5. Qleenie

    Qleenie

    Joined:
    Jan 27, 2019
    Posts:
    868
    I’d be interested, too! Working in the exact same thing.
     
    Knightspear and clamum like this.
  6. clamum

    clamum

    Joined:
    May 14, 2017
    Posts:
    61
    So my current working game has the following relevant parts of code. It features people models walking around a city, via NavMesh/NavMeshAgent, and an object falling from the sky and when that object hits the people, it knocks them around according to physics.

    The people simply use NavMesh in the following manner (CurrentDestination is a random location on the game board, calculated previously):

    Code (CSharp):
    1.  
    2. private void Start()
    3.     {
    4.         path = new NavMeshPath();
    5.         NavMeshAgent = GetComponent<NavMeshAgent>();
    6.         rigidbody = GetComponent<Rigidbody>();
    7.  
    8.         bool pathFound = NavMeshAgent.CalculatePath(CurrentDestination, path);
    9.  
    10.         if (pathFound)
    11.         {
    12.                NavMeshAgent.SetPath(path);
    13.         }
    14. }
    The people objects have the following Components attached to them: NavMeshAgent, BoxCollider, Rigidbody.

    The object falling from the sky is a simple sphere with a SphereCollider and Rigidbody attached to it.

    For the code for the falling object, I have the following:

    Code (CSharp):
    1. void OnCollisionEnter(Collision collision)
    2.     {
    3.         HitPosition = gameObject.transform.position;
    4.  
    5.         if (collision.gameObject.tag == "Person")
    6.         {
    7.             if (!hasPlopped) // just a flag to indicate if falling object has hit person
    8.             {
    9.                 hasPlopped = true;
    10.                 hasPersonCollisionOccurred = true;
    11.                 collisionPerson = collision.gameObject.GetComponent<Person>();
    12.  
    13.                 StartCoroutine(TheMainGameMethod());
    14.             }
    15.         }
    16. }
    17.  
    18. private void FixedUpdate()
    19.     {
    20. if (hasPersonCollisionOccurred)
    21.         {
    22.             if (collisionPerson != null)
    23.             {
    24.                 collisionPerson.NavMeshAgent.enabled = false;
    25.                 collisionPerson.rigidbody.isKinematic = false;
    26.  
    27.                 collisionPerson.rigidbody.AddForce(100, 100, 100, ForceMode.Force); // knock the hit person around a bit
    28.  
    29.                 hasPersonCollisionOccurred = false;
    30.             }
    31.         }
    32.         // toggle NavMesh back on for hit person (when they stop moving from the hit)
    33.         else if (collisionPerson != null)
    34.         {
    35.             float personVelocity = collisionPerson.rigidbody.velocity.magnitude;
    36.  
    37.             // close enough to zero; toggle NavMesh back on
    38.             if (personVelocity < 0.5f)
    39.             {
    40.                 collisionPerson.NavMeshAgent.enabled = true;
    41.                 collisionPerson.rigidbody.isKinematic = true;
    42.             }
    43.         }
    44.     }
    This works mostly well although after a person gets hit and they get knocked around, they seem to stay still for a bit before they start moving again (they're supposed to pick a new random location on the game board and move to it). However that's not a huge deal to me at this point and I may address that later if I feel it necessary.

    I think that's about it, for the main components to this NavMesh/physics thing anyway. Let me know if you have any questions, and hopefully this helps someone.

    EDIT: Looking at the code again, I should probably set "collisionPerson" to NULL after they are hit (say, on line 42 at the end of the FixedUpdate() method) but it seems to work fine right now.