Search Unity

Resolved Problems with initial synchronization of NavMeshAgent with netcode.

Discussion in 'Netcode for GameObjects' started by OLASCHE, Jan 12, 2023.

  1. OLASCHE

    OLASCHE

    Joined:
    Oct 6, 2016
    Posts:
    3
    I have the following PlayerController script attached to a NetworkObject to move a NavMeshAgernt towards a click in a multiplayer server authoritative game. The NetworkObject also has a NetworkTransform attached to it.
    Code (CSharp):
    1.  
    2. using Unity.Netcode;
    3. using UnityEngine;
    4. using UnityEngine.AI;
    5.  
    6. public class PlayerController : NetworkBehaviour
    7. {
    8.     public LayerMask groundMask;
    9.     public NavMeshAgent agent;
    10.     public Camera camera;
    11.  
    12.     void Update()
    13.     {
    14.         if (!IsOwner) return;
    15.         if (Input.GetMouseButtonDown(1))
    16.         {
    17.             Ray camMouseRay = camera.ScreenPointToRay(Input.mousePosition);
    18.             RaycastHit hitInfo;
    19.             if (Physics.Raycast(camMouseRay, out hitInfo, 100, groundMask))
    20.             {
    21.                 SetAgentDestinationServerRpc(hitInfo.point);
    22.             }
    23.         }
    24.     }
    25.  
    26.     [ServerRpc]
    27.     public void SetAgentDestinationServerRpc(Vector3 destination)
    28.     {
    29.         agent.destination = destination;
    30.     }
    31. }
    32.  
    The code seems to work fine except when a new client joins, the position of old clients and hosts is not immediatley syncronized, only when the other clients make their first move do you see any movement in the new client (which then seems to be synchronized). Regardless of whether the old clients move or not, the new one does appear synchronized from the start on both the old clients and host.

    Am I doing something wrong? Or is this expected behaviour? If so how can i modify the code to update all agents positions on load.
     
    Last edited: Jan 12, 2023
  2. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    440
    Hi @OLASCHE , the NetworkTransform should definitely synchronize players's positions automatically.

    However, I'm not sure it has any interaction with navmeshes and I see that previous versions of Netcode For GameObjects had a NetworkNavMeshAgent component, which has now been removed (probably because you don't need to have the NavMeshAgent component at all on clients if the movement is server-authoritative).

    Let me confirm my understanding on this, so I can help you:

    Do you mean that when a new client joins, its position is not immediatley syncronized on old clients and hosts?
    Or do you mean that the position of old clients and hosts is not synchronized on the new client's game instance?

    What might be happening in your case is that the navmeshagents on the clients don't have their destination set, and therefore don't know where to go.
    What you could try to do is:

    1. Keep the navmeshagent component active only on the server (you can disable it on the prefabs and enbale it in OnNetworkSpawn if it's the server)
    2. Make the destination a syncvar, and call agent.SetDestination(destination); in OnNetworkSpawn

    Does this help?
     
    OLASCHE likes this.
  3. OLASCHE

    OLASCHE

    Joined:
    Oct 6, 2016
    Posts:
    3
    Hello Riku, thanks for your response and taking the time to help me out.

    I meant the second option, the position of old clients and hosts is not synchronized on the new client's game instance, but only at the very start, once new movements come in they synchronize.

    I tried both of the suggestions;
    1. The first suggestion I tried by removing the navmeshagent component on non server instances, but this did nothing, the final product was still behaving the same (Synching everything except existing network transforms initial positions at the start of a new client joining)
    2. I am not familiar with syncvars but after looking into it a bit it seems like its from the Mirror Library and as far as i could tell using NetworkVariables is the same, please correct me if im wrong. Using Network variables i managed to fix the issue, the code looks like this:

    Code (CSharp):
    1.  
    2. using Unity.Netcode;
    3. using UnityEngine;
    4. using UnityEngine.AI;
    5.  
    6. public class PlayerController : NetworkBehaviour
    7. {
    8.     public LayerMask groundMask;
    9.     public NavMeshAgent agent;
    10.     public Camera camera;
    11.  
    12.     public NetworkVariable<Vector3> _destination = new(writePerm: NetworkVariableWritePermission.Server);
    13.  
    14.     public override void OnNetworkSpawn()
    15.     {
    16.         if (!IsServer & !IsHost)
    17.             Destroy(agent.GetComponent<NavMeshAgent>());
    18.         if(IsClient)
    19.             agent.transform.position = _destination.Value;
    20.     }
    21.  
    22.     void Update()
    23.     {
    24.         if (!IsOwner) return;
    25.         if (Input.GetMouseButtonDown(1))
    26.         {
    27.             Ray camMouseRay = camera.ScreenPointToRay(Input.mousePosition);
    28.             RaycastHit hitInfo;
    29.             if (Physics.Raycast(camMouseRay, out hitInfo, 100, groundMask))
    30.             {
    31.                 SetAgentDestinationServerRpc(hitInfo.point);
    32.             }
    33.         }
    34.     }
    35.  
    36.     [ServerRpc]
    37.     public void SetAgentDestinationServerRpc(Vector3 destination)
    38.     {
    39.         agent.destination = destination;
    40.         _destination.Value = destination;
    41.     }
    42. }
    43.  

    Now this seems to work, but this leaves me with 2 questions:
    1. If i have hundreds of network objects, wont adding a network variable to it make the network load much bigger? Specially since the only real use is for when a new player joins.
    2. As i mentioned before, maybe i completely misunderstood and this isnt the approach you meant, and i do need to use the mirror library with the syncvar.
    Thanks for any clarification in advance.
     
    Last edited: Jan 12, 2023
  4. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    440
    Hi @OLASCHE , glad to see that you managed to solve the issue! :D

    Ok, it's definitely solvable with NetworkVariables. Basically the problem is that Agents on the new client have no idea where to go. Once an agent gets a new movement command, everything self-fixes. This is expected if you're using RPCs instead of NetworkVariables.

    When I mentioned NetworkVariables I referred to Netcode for GameObjects ("NGO") ones, which you can have a look at here. Mirror also has NetworkVariables (they're a common concept across multiplayer frameworks) and they work pretty much in the same way as NGO's.

    By looking at your code, you implemented them the correct way.

    1. If you have hundreds of NetworkObjects, you probably have to review your design. 90% of the time, the only things you need to synchronize in a multiplayer game are the player's characters, and everything else can be synchronized through a single network object that holds data (health, position etc...) for all the object you think you might need to synchronize (I.E: minions in a MOBA game). The clients will then use the data from this object to draw/position local, non-networked objects that represent the entities. This article can clarify what I just said :D

    Apart from that, NetworkVariables are only sent over the network only when their value changes, so if they're changing rarely you should be fine. My advice here is to worry about optimization only when it becomes a bottleneck.

    Pro tip: I see that you're syncing a Vector3, but from the code I see you're picking the destination by clicking. Is this a MOBA? In that case, you might only need to sync a Vector2, which is faster and cheaper.


    You don't need Mirror if you're using NGO, and I was referring to NGO's NetworkVariables

    Hope this helps ^.^
     
    OLASCHE likes this.
  5. OLASCHE

    OLASCHE

    Joined:
    Oct 6, 2016
    Posts:
    3
    Thanks again Raiku for all the help.
    Yes, im developing a MOBA-RTS, which is why im imagining quite a bit of unit controlls, closer to a total war game, ill take a look at the link you provided since it seems like a good read.

    The way I implemented it it would be changing every click which could be a lot, I guess I can continue with development since there doesnt seemsto be a bottleneck so far, i just wanted to get my foundations right before proceeding.


    I agree, in fact i was gonna use a custom struct, but i just simplified the code for the question.
     
    RikuTheFuffs-U likes this.
  6. AlexaRebecca

    AlexaRebecca

    Joined:
    Dec 14, 2018
    Posts:
    26
    Hello, I had the same problem and I managed to solve it, but I use unity 2022 and so that it did not give me an error, I had to use the old version, the navigation which is obsolete because with NavMeshsurf it did not solve it