Search Unity

NavMeshAgent for player character movement (Mobile, Joystick)

Discussion in 'Scripting' started by Megamike55, Feb 4, 2014.

Thread Status:
Not open for further replies.
  1. Megamike55

    Megamike55

    Joined:
    Sep 3, 2012
    Posts:
    4
    What the system is. Definition of problem.
    I am developing a mobile game without using physics (except for trigger volumes). The player moves purely on a navmesh, using Unity's built-in navmesh and NavmeshAgents. The player has an on-screen joystick, and so movement is done every Update() rather than in a click-to-move style. The fact that I must update the player's heading every Update() tick seems to be part of my problem in creating a Player character which is able to smoothly collide with obstacles and other navmesh agents.

    Previous Research
    I read a great thread:
    http://forum.unity3d.com/threads/130012-Need-Advice-or-Recommendations-with-NavMesh-and-Agents
    In which AngryAnt so helpfully explains a good way to implement movement in such a system. As we build our game, I have used (kind of) his approach, and also another approach of just using SetDestination(pos). Each approach seems to have its own flaws, and I cannot seem to find a perfect, smooth solution.

    Approach #1 flaws
    When using the (kind of) approach suggested by AngryAnt (IE: Calling .Move(pos)), The overall movement of the agent is very jittery (but no rubber-banding). I have the script setup with: navAgent.updatePosition = true;

    Approach #2 flaws
    When using the more simplistic .SetDestination approach, I get rubber-banding under certain conditions (the conditions are not clear to me, but mostly the rubberbanding only happens when the system is under heavy load, like when I am running full-screen image capture at the same time as simulating the game in Unity).

    Approach #3 flaws
    I expanded the 2nd approach to use the NavMesh's raycast function to try to resolve the rubberbanding. I thought that perhaps the rubberbanding was due to trying to set a destination outside the navmesh bounds. However, making sure that my .SetDestination() calls always pointed to a position on the navmesh did not resolve the problem.

    Summary
    I have tried the above 3 approaches with various different sets of NavMeshAgent script settings, like enabling or disabling AutoBraking or AutoRepath and so forth. AutoBraking seems to have the biggest impact, but since Unity's NavMeshAgent code is not visible, I really cannot tell what Unity is doing behind the scenes with all those attributes, and it does not appear that the NavMesh or NavMeshAgent scripts expose any way to detect other agents or obstacles, so I must rely on the NavMeshAgent script itself to update my character's position, which makes all those attributes still quite relevant (even though in reality they are irrelevant, since I am not trying to do pathfinding)

    If anyone has any insight into how to properly set this up (again, not using physics, using a joystick to move, no jitter, no rubber-banding, colliding with other Agents + Obstacles, and also preferably sliding around obstacles), I greatly appreciate it.

    If the answer to this inquiry is that I must track all NavMeshAgents and NavMeshObstacles myself, and raycast against the navmesh, etc. (effectively duplicating the NavMeshAgent script, minus pathfinding), then I will, but I want to be sure I haven't missed something first.

    Thanks!
     
    Last edited: Feb 4, 2014
  2. MiguelKing

    MiguelKing

    Joined:
    Apr 28, 2015
    Posts:
    5
    Since nobody replied this post, I will share what worked for me for a Joystick controlled agent. From the joystick motion I calculate a motionVector (velocity vector) proportional to Time.deltaTime and the desired character speed (the one set in NavMeshAgent.speed). Then I move the agent using:

    NavAgent.Move(motionVector);
    NavAgent.SetDestination(transform.position+motionVector);
     
  3. ssdigi

    ssdigi

    Joined:
    Dec 20, 2015
    Posts:
    3
  4. m0ty0

    m0ty0

    Joined:
    Mar 27, 2020
    Posts:
    1
    here's video that helped me :


    In combination with what MiguelKing said I've settled on something like this:

    Code (CSharp):
    1.     Vector3 movement;
    2.     NavMeshAgent agent;
    3.  
    4.     // Start is called before the first frame update
    5.     void Start()
    6.     {
    7.         agent = GetComponent<NavMeshAgent>();
    8.     }
    9.  
    10.     // Update is called once per frame
    11.     void Update()
    12.     {
    13.         float horizontalInput = Input.GetAxisRaw("Horizontal");
    14.         float verticalInput = Input.GetAxisRaw("Vertical");
    15.  
    16.         movement.Set(horizontalInput, 0f, verticalInput);
    17.  
    18.         agent.Move(movement * Time.deltaTime * agent.speed);
    19.         agent.SetDestination(transform.position + movement);
    20.     }
    Hope it helps
     
    TylerCode_ likes this.
  5. TylerCode_

    TylerCode_

    Joined:
    Sep 8, 2010
    Posts:
    221
    Stumbled onto this by chance. One last thing I noticed that seemed to only happen in editor was stuttering when using the Move function. My game isn't super sensitive to a little sponginess in controls so I ended up turning off NavMeshAgent.updatePosition and handling that myself. I'm pretty happy with the result and appreciate this entire thread!


    Code (CSharp):
    1. private float _moveSmoothing = 0.3f;
    2. private NavMeshAgent _agent;
    3.  
    4. void Start()
    5. {
    6.     _agent = GetComponent<NavMeshAgent>();
    7.     _agent.updatePosition = false;
    8. }
    9.  
    10. void Update()
    11. {
    12.     float horizontal = Input.GetAxis("Horizontal");
    13.     float vertical = Input.GetAxis("Vertical");
    14.  
    15.     Vector3 movement = new Vector3(horizontal, 0, vertical);
    16.  
    17.     _agent.Move(movement * Time.deltaTime * _agent.speed)
    18.  
    19.     this.transform.position = Vector3.Lerp(this.transform.position, _agent.nextPosition, _moveSmoothing);
    20. }
     
    ryanmillerca likes this.
  6. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    Here's something I did recently, basicly it rotates to the right direction then moves. Seems to work for both point and click and direct control or a mix.

    Maybe your game doesn't need that, but it comes out pretty much 100% smooth and animated (assuming you have correct belnds set on your character animator):

    Code (csharp):
    1.  
    2.  
    3.         protected override void LateUpdate()
    4.         {
    5.             _agent.nextPosition = transform.position;
    6.         }
    7.  
    8.         // Assumes agent target destination has been set, works best if agent has high acceleration
    9.         // For direct control set the target direction at pos + input dir * k (where k == 2 for me, but adjust for your game)
    10.  
    11.         private void Move()
    12.         {
    13.             // Rotate towards direction we need to head
    14.             var desiredVelocity = _agent.desiredVelocity;
    15.             var dir = desiredVelocity.normalized;
    16.             var targetSpeed = desiredVelocity.magnitude;
    17.             if (desiredVelocity != Vector3.zero)
    18.             {
    19.                 // Look towards target dir
    20.                 var lookRotation = Quaternion.LookRotation(dir);
    21.                 _transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, _agent.angularSpeed * Time.deltaTime);
    22.                 var rotationDifference = Quaternion.Angle(_transform.rotation, lookRotation);
    23.  
    24.                 // Adjust speed based on angle
    25.                 if (Mathf.Abs(rotationDifference) > 45)
    26.                 {
    27.                     //  Bigger than 45 and we can't move forward and instead play a rotating animation
    28.                     targetSpeed = 0;
    29.                     _animator.SetBool(QuickRotateParam, true);
    30.                 }
    31.                 else
    32.                 {
    33.                     targetSpeed *= Mathf.Cos((Mathf.Deg2Rad * 2 )* rotationDifference);
    34.                 }
    35.             }
    36.             else
    37.             {
    38.                 targetSpeed = 0;
    39.             }
    40.  
    41.             // Accelerate to target speed
    42.             if (_currentSpeed > targetSpeed)
    43.             {
    44.                 _currentSpeed += Time.deltaTime * deceleration;
    45.             }
    46.             else
    47.             {
    48.                 _currentSpeed += Time.deltaTime * acceleration;
    49.             }
    50.  
    51.             // Move
    52.             _transform.position += _transform.forward * (Time.deltaTime * _currentSpeed);
    53.             _animator.SetFloat(ForwardSpeedParam, _currentSpeed);
    54.         }
    55.     }
    56. }
    57.  
     
    TylerCode_ likes this.
Thread Status:
Not open for further replies.