Search Unity

How to send costum data or NetworkObject ?

Discussion in 'Netcode for GameObjects' started by edin97, Mar 11, 2022.

  1. edin97

    edin97

    Joined:
    Aug 9, 2021
    Posts:
    58
    I have this issue where I instantiate a NetworkObject prefab on the server and use SpawnWithOwnership to spawn it on the client. I'm using
    Code (CSharp):
    1. ParticlePrefabInstance = Instantiate(...)
    to store the prefab on the server side inside of ParticlePrefabInstance which is also a NetworkObject. Now, when I do :
    Code (CSharp):
    1. ParticlePrefabInstance.SpawnWithOwnership(OwnerClientId);
    How can I do the same for the client and store the instantiated prefab inside of his own "ParticlePrefabInstance" variable ??? Or how to send that data ? because it's a costum data and I don't know how to serialize it. I need this to change a variable in a script attached to the NetworkObject prefab.
     
  2. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    I may not be fully understanding what you're after but here are some options. You could grab Particle in its Awake, Start or OnNetworkSpawn call, see which works best for you. From there you can get the other script component and pass what you want to it. Something like this should work:

    Code (CSharp):
    1.    public override void OnNetworkSpawn()
    2.         {
    3.             base.OnNetworkSpawn();
    4.  
    5.             if (IsOwner)
    6.             {
    7.                  OtherScript other = GetComponent<OtherScript>();
    8.                  other.AddValue(this.value);
    9.             }
    10.         }
     
    Last edited: Mar 11, 2022
  3. edin97

    edin97

    Joined:
    Aug 9, 2021
    Posts:
    58
    Thank you for the answer, but I might have explained myself poorly. Both my client and host have this script : "PlayerSpellCasting", simplified version contains this :
    Code (CSharp):
    1. private NetworkObject particlePrefabInstance;
    2. [SerializeField] private NetworkObject particlePrefab;
    3. private RaycastHit hit
    4.  
    5.     [ServerRpc]
    6.     private void SpellCastOnStateServerRpc()
    7.     {
    8.          // Instantiating the particle prefab on the server side and keeping a reference in ParticlePrefabInstance
    9.          ParticlePrefabInstance = Instantiate(particlePrefab, transform.position, transform.rotation).GetComponent<NetworkObject>();
    10.          // Instantiating particle for clients without keeping a reference to it
    11.          ParticlePrefabInstance.SpawnWithOwnership(OwnerClientId);
    12.           // change value ClickedOnPosittion (Vector3) to the position the player clicked on with his mouse (Vector3)
    13.           ParticlePrefabInstance.gameObject.GetComponent<Spell>().ClickedOnPosition = hit.point;
    14.     }
    15.  
    The client calls this ServreRpc to instantiate the particle, and the host has its own method for doing this without the ServerRpc
    Now, since ONLY the server is allowed to instantiate a prefab, only the server can store the prefab in a reference, here I'm storing my "particlePrefab" in "ParticlePrefabInstance" after instantiating it. from there I can change the component "ClickedOnPosition" in the "Spell" script, but the client is not able to do that since only the server can use the method "Instantiate" and keep a reference to the instantiated object. which is why this works on the server side :
    Code (CSharp):
    1.        
    2. // change value ClickedOnPosittion to the position the player clicked with his mouse on
    3. ParticlePrefabInstance.gameObject.GetComponent<Spell>().ClickedOnPosition = hit.point;
    But can't be done on the client side because "ParticlePrefabInstance" is null on its side.

    Thank you for the quick answer again, I did find a workaround since I asked the question which is changing the ClickedOnPosition for the client on the server because somehow "ParticlePrefabInstance" is correct in the server side for both players, but I still want to know if there is a way to give the client a reference to its own prefab instance, which would make everything easier
     
    Last edited: Mar 11, 2022
  4. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    Yes, there is a way to do this. You can send a
    NetworkObjectReference
    through a ClientRpc and get the GameObject (or anything else attached to the NetworkObject) through that. See here.
     
    cerestorm and edin97 like this.
  5. edin97

    edin97

    Joined:
    Aug 9, 2021
    Posts:
    58
    Wow thank you so much !! idk how I missed that
     
  6. liambilly

    liambilly

    Joined:
    May 13, 2022
    Posts:
    154
    did you find a solution here, what did you do to assign .ClickedOnPosition for the client
     
  7. edin97

    edin97

    Joined:
    Aug 9, 2021
    Posts:
    58
    Well, I don't really remember since this was 1 year ago, sorry. But for this case I would change the .ClickedOnPosition variable to a NetworkVariable, so when the server changes it, it applies to the clients too. So, probably something like :
    Code (CSharp):
    1.         [SerializeField] private NetworkVariable<Vector3> ClickedOnPosition = new NetworkVariable<Vector3>();
    2.  
    And then you do at line 13:
    Code (CSharp):
    1. ParticlePrefabInstance.gameObject.GetComponent<Spell>().ClickedOnPosition.Value = hit.point;
    I cannot confirm this will work, but it should.
    In my case, I didn't want the ClickedOnPosition to be a NetworkVariable (I think). So, you can do something like this:
    Code (CSharp):
    1.  
    2.         [ClientRpc]
    3.         private void ChangeClickedPositionClientRpc(NetworkObjectReference ParticlePrefab_NOR, Vector3 clickedPos)
    4.         {
    5.             if (IsServer) { return; }
    6.             if (ParticlePrefab_NOR.TryGet(out NetworkObject ParticlePrefab_NO))
    7.             {
    8.                 ParticlePrefab_NO.GetComponent<Spell>().ClickedOnPosition = clickedPos;
    9.             }
    10.         }
    and call it after line 13 for example in the script I gave above:
    Code (CSharp):
    1.             ChangeClickedPositionClientRpc(ParticlePrefabInstance, hit.point);
    2.  
    hopefully this helps !
     
  8. liambilly

    liambilly

    Joined:
    May 13, 2022
    Posts:
    154
    in my case im trying to send information about direction to a script on a projectile that im spawning, im not sure if i can use a networkvariable on a script that has no owenership;
    this is the code on the projectile
    Code (CSharp):
    1. [ServerRpc(RequireOwnership = false)]
    2.     public void ShowTrailServerRpc(Vector3 startPos, Vector3 hitpoint)
    3.     {
    4.         ShowTrailClientRpc(startPos);
    5.     }
    6.     [ClientRpc]
    7.     public void ShowTrailClientRpc(Vector3 startPos)
    8.     {
    9.         StartCoroutine(ShowTrail(startPos));
    10.         Debug.Log(trgt);
    11.         print($"projectile{hitpoint}");
    12.     }
    13.     public IEnumerator ShowTrail(Vector3 startPos)
    14.     {
    15.         yield return new WaitUntil(() => startPos != Vector3.zero);
    16.         float time = 0;
    17.         while (time < 1)
    18.         {
    19.             transform.position = Vector3.Lerp(startPos, hitPoint, time);
    20.             time += Time.deltaTime / trail.time;
    21.             yield return null;
    22.         }
    23.         //hit effect;
    24.     }
    so i called it on the players script like this
    Code (CSharp):
    1. [ServerRpc(RequireOwnership = false)]
    2.     void ProjectileServerRpc(Vector3 direction)
    3.     {
    4.         Projectile2(direction);
    5.     }
    6.  
    7.     void Projectile2(Vector3 direction)
    8.     {
    9.         GameObject go = Instantiate(prefab, startpos.position, startpos.rotation);
    10.         var no = go.GetComponent<NetworkObject>();
    11.         no.Spawn();
    12.         ShowTrailClientRpc(startpos.position,  no);
    13.     }
    14.     [ClientRpc]
    15.     public void ShowTrailClientRpc(Vector3 startPos, NetworkObjectReference nor)
    16.     {
    17.         //if (IsServer) { return; }
    18.         if (nor.TryGet(out NetworkObject netobj))
    19.         {
    20.             netobj.GetComponent<TestProjectile>().hitPoint = hit.point;
    21.             StartCoroutine(netobj.GetComponent<TestProjectile>().ShowTrail(startPos));
    22.             netobj.GetComponent<TestProjectile>().trgt = hit.transform;
    23.         }
    24.     }
    now the projectile will travel in the ryt direction but only locally, other client see it travelling to vector 0,0,0
     
  9. unityenjoyeronlywhenitworks

    unityenjoyeronlywhenitworks

    Joined:
    May 6, 2023
    Posts:
    3
    Not sure I understand everything, but I think the "hit.point" variable is only assigned locally ? before you call "ShowTrailClientRpc" are you assigning "hit.point" when a player clicks with his mouse somewhere ? If so, the inputs are local, you probably used something like "if (IsOwner) (... get input ....)". which means only the player that clicked the mouse will have: "hit.point = something that is not (0, 0, 0)", but the other clients never get that information. Which might explain why it only works locally. because if it's not assigned, it will always go to (0, 0, 0).

    Maybe when you call "ProjectileServerRpc(..., Vector3 hitPoint)" you need to give "hit.point" as a variable ? Then "Projectile2(..., Vector3 hitPoint)" And then when you call "ShowTrailClientRpc(..., Vector3 hitPoint)" too, you give it the hit point and instead of "netobj.GetComponent<TestProjectile>().hitPoint = hit.point;" you make it "= hitPoint" but I really don't know I'm just guessing.

    You are allowed to change the NetworkVariable using the server (even without ownership) so you can call a ServerRpc from the client to change the NetworkVariable and then the server will change it for everyone automatically. (in newer version I think you don't need the Server to change the NetworkVariable, but I haven't used the newer versions yet and it might require ownership.)