Search Unity

Root Motion with a NavmeshAgent walking through walls

Discussion in 'Animation' started by smonz3, Apr 19, 2017.

  1. smonz3

    smonz3

    Joined:
    Jan 9, 2017
    Posts:
    18
    Hi, I am trying to use root motion for my humanoid enemy AI characters which use a unity navmesh. I'm running in to a lot of issues with the charachters walking through walls and objects even though they are clearly baked onto the navmesh. I've been doing a bit of searching and it seems like root motion will overrule all colliders etc? is this correct? If so is there any way I can stop my characters running through walls and obstacles?

    Part of the issue seems to be that the navmesh agent doesn't nicely follow the character, instead it lags behind or pushes ahead unless it is pulled back using :
    Code (CSharp):
    1.         // Pull agent towards character
    2.         if (worldDeltaPosition.magnitude > agent.radius)
    3.             agent.nextPosition = transform.position + 0.9f*worldDeltaPosition;
    however this doesn't keep the agent centred with the character, usually the character will sit on the edge of the actual agent radius, meaning they often end up walking or getting pushed into walls. Currently i'm using the above code but with 0.01f*worldDeltaPosition and that seems to have given me the best results.. however there are still heaps of issues, especally when getting the enemy to move to specific positions in the world, such as cover, most of the time the enemy will end up inside the cover position... rather than behind it.

    one of the causes is possibly that the agent stops updating when the character is stopping, but as the animations blend between walking/running and idle over a second or so, the actual character ends up further forward of the agent, this often ends up with the agent in a wall or past the point it needs to be.

    I've tried increasing the agent radius but this doesn't help, if anything it makes it more error prone. I've also set agent.Warp to be called every few frames and that helps but it messed up a lot of other stuff, I guess it recalculates the path every time it happens? currently I have it called every second if the character is stopped in order to make sure the agent and character are aligned when it starts moving again...

    I also tried the other unity suggestion of pulling the character towards the agent but this gave pretty unusable results.

    My code is more or less the same as the code from this tutorial https://docs.unity3d.com/Manual/nav-CouplingAnimationAndNavigation.html

    I'm really struggling with this as I'm in the process of trying to code a cover system for my AI but I can never get it to stop in the right place. Has anyone got any suggestions or has anyone used root motion with a navmesh agent before for AI characters, especially ones that need to navigate "busy" areas?
     
  2. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    It hard without having the full code to see what could be wrong.

    But normally if your implementation depends on the transform position, this code should be moved into OnAnimationMove callback which happen after MonoBehaviour.Update(), doing it in Update is like trying to match the position from the previous frame since the animation is not yet updated for this frame.
    see function execution order for more detail
    https://docs.unity3d.com/Manual/ExecutionOrder.html
     
  3. hsjaaa

    hsjaaa

    Joined:
    Apr 30, 2016
    Posts:
    29
    hi there, I'm having some issue coupling navigation with root motion, tried to search the internet but find very few information besides the doc.
    Code (CSharp):
    1. void UpdateAnimator()
    2. {
    3.     Vector3 worldDeltaPosition = navAgent.nextPosition - transform.position;
    4.  
    5.     worldDeltaPosition.y = 0f;
    6.     // Map 'worldDeltaPosition' to local space
    7.     float dx = Vector3.Dot(transform.right, worldDeltaPosition);
    8.     float dy = Vector3.Dot(transform.forward, worldDeltaPosition);
    9.     Vector2 deltaPosition = new Vector2(dx, dy);
    10.  
    11.     // Low-pass filter the deltaMove
    12.     float smooth = Mathf.Min(1.0f, Time.deltaTime / 0.15f);
    13.     smoothDeltaPosition = Vector2.Lerp(smoothDeltaPosition, deltaPosition, smooth);
    14.  
    15.     // Update velocity if time advances
    16.     if (Time.deltaTime > 1e-5f)
    17.         velocity = smoothDeltaPosition / Time.deltaTime;
    18.  
    19.     bool shouldMove = velocity.magnitude > 0.5f && navAgent.remainingDistance > navAgent.radius;
    20.  
    21.     // Update animation parameters
    22.     animator.SetBool("move", shouldMove);
    23.  
    24.     animator.SetFloat("velx", velocity.x);
    25.     animator.SetFloat("vely", velocity.y);
    26.     // Pull character towards agent
    27.     if (worldDeltaPosition.magnitude > navAgent.radius)
    28.         transform.position = navAgent.nextPosition - 0.9f * worldDeltaPosition;
    29. }
    30.  
    31. void OnAnimatorMove()
    32. {
    33.     // Update position based on animation movement using navigation surface height
    34.     Vector3 position = animator.rootPosition;
    35.     position.y = navAgent.nextPosition.y;
    36.     transform.position = position;
    37. }
    this code is bassically from doc, tried to call the updateAnimator method in the update or on animator move, but the speed feed to animator is always wrong and too high, make agent running wild. If not use root motion then the animation is fine. Any idea? Thanks.