Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Can't send a transform to the client

Discussion in 'Netcode for GameObjects' started by ErezShahaf, Apr 14, 2022.

  1. ErezShahaf

    ErezShahaf

    Joined:
    Feb 3, 2022
    Posts:
    70
    Hi guys, I can't figure out how to send back a Transform to the client from the server:
    Code (CSharp):
    1. [ServerRpc]  
    2. void spawnNewPlayer()
    3.     {
    4.         vr_rig = gameObject.GetComponent<VRRig>();
    5.         xrRigInstace = Instantiate(XRRigPrefab, playerCourtPosition, playerCourtRotation);
    6.         gunInstance = Instantiate(GunPrefab, Vector3.zero, Quaternion.identity);
    7.         xrRigInstace.GetComponent<NetworkObject>().Spawn();
    8.         gunInstance.GetComponent<NetworkObject>().Spawn();
    9.         right_hand_sensor = xrRigInstace.transform.Find("RightController");
    10.         left_hand_sensor = xrRigInstace.transform.Find("LeftController");
    11.         head_sensor = xrRigInstace.transform.Find("Camera Offset").transform.Find("HeadSensor");
    12.         uppdateVrTargetClientRpc(right_hand_sensor, left_hand_sensor, head_sensor);
    13.     }
    14.     [ClientRpc]
    15.     void uppdateVrTargetClientRpc(Transform right_hand_sensor_target, Transform left_hand_sensor_target, Transform head_sensor_target)
    16.     {
    17.         vr_rig.leftHand.vrTarget = left_hand_sensor_target;
    18.         vr_rig.rightHand.vrTarget = right_hand_sensor_target;
    19.         vr_rig.head.vrTarget = head_sensor_target;
    20.     }
    The relevant lines are:

    Code (CSharp):
    1.   uppdateVrTargetClientRpc(right_hand_sensor, left_hand_sensor, head_sensor);
    And:

    Code (CSharp):
    1. void uppdateVrTargetClientRpc(Transform right_hand_sensor_target, Transform left_hand_sensor_target, Transform head_sensor_target)
    The error I get is:

    Code (CSharp):
    1.  error  - Don't know how to deserialize Transform - implement INetworkSerializable or add an extension method for FastBufferReader.ReadValueSafe to define serialization.
    Does it mean that clientRpc does not support Transform? I also tried to define the transforms as networkVariables but it also didn't support it.

    Do I need to write my own methods? doesn't seem right..
     
  2. yosimba2000

    yosimba2000

    Joined:
    Jun 3, 2021
    Posts:
    25
    Transform is class, so it is a "pass by reference" value type. So
    Code (CSharp):
    1. Transform right_hand_sensor
    actually holds the value of a memory location (like 0x123), not the actual Transform values (like position x, y ,z). That memory location (0x123) represents ANOTHER memory location, but this second memory location is the one that actually holds Transform values you are interested in.


    So in this code:
    Code (CSharp):
    1. uppdateVrTargetClientRpc(right_hand_sensor, left_hand_sensor, head_sensor);
    ,
    you are not actually giving ClientRpc the actual Transform values, but rather the server's memory location (0x123) that holds the Transform true values.

    So, this means two things.

    1) You cannot pass the server's memory location to the client (and even if you could, you shouldn't), because the memory location 0x123 on the Server represents something completely unrelated to the memory location 0x123 on the Client. It's like saying you have two buildings, each building has room labeled 0x123. But for the first building, room 0x123 is used a storage location; The second building uses room 0x123 as a bedroom.

    2) You need to pass the actual value to the ClientRpc, not a memory location. So instead of sending a "pass by reference" data type, you need to send the actual value, AKA a "pass by value" data type. All basic data types like ints, floats, strings, etc are "pass by value". So if you send an integer to ClientRpc, it will send that actual integer value to ClientRpc, not a memory location which holds the value of the integer.

    Structs are also "pass by value" data types. Lucky for you, Vector3 is a struct, so you can pass a Vector3 data type to the ClientRpc and it will do as you expect. You will have to obtain the Vector3 by going
    Code (CSharp):
    1. Vector3 rightHandPosition = right_hand_sensor.gameObject.transform.position;
    What is happening here is that we are creating a new "pass by value" variable of type Vector3 (and we know this variable is "pass by value" data type because Vector3 is a struct, and all structs are "pass by value" data types), and setting it equal to the Transform object's variables (the .position, and .position is a "pass by value" because it's also a Vector3).

    If you want to send any data over the network, you must send "pass by value" data types, not "pass by reference" data types.

    All classes are "pass by reference" data types, and all structs (along with stuff like int, string, etc) are all "pass by value" data types.

    So if something you want to send is held in "pass by reference" data type, you need to find a way to convert/extract it to a "pass by value" data type, or specifically specify the full path that leads to the "pass by value" data type. For example
    Code (CSharp):
    1. aString = SomeStringClassObject.someText;
     
    Last edited: Apr 14, 2022
    TinMRD and ErezShahaf like this.
  3. ErezShahaf

    ErezShahaf

    Joined:
    Feb 3, 2022
    Posts:
    70
    That's a perfect explanation, thank you so much!
    Actually, in my case I insisted on transform because I then later have to attach the "VR target" to every user that joins the game:
    upload_2022-4-15_0-29-57.png
    I believe that vector3 may be problematic because I want to attach the transform of a gameobject to the Vr target so that it can keep following it in my VRRig script.

    Perhaps there is a better way to do it? gameobjects are also a class so I will come across the same issue.

    One other thing that I could do just spawn the "sensors" with the server and then find them on the client. The problem is that I'm not sure how to find them as there will be many objects with the exact same name and I do not get the instance or the PrefabHash from the server

     
    Last edited: Apr 14, 2022
  4. ErezShahaf

    ErezShahaf

    Joined:
    Feb 3, 2022
    Posts:
    70
    I think that I'll just add a tag to the gameobjects that the server creates and then move that tag from the server to the client.. It will solve the problem but it's kinda weird that its not possible to send Transforms
     
  5. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    I'm no expert but in lieu of a better answer. You might be able to get NetworkTransform and ClientNetworkTransform to help you out here. That's if you set it up to have your sensors as separate network objects.
     
  6. ErezShahaf

    ErezShahaf

    Joined:
    Feb 3, 2022
    Posts:
    70
    They already are
     
  7. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    So why are you sending the transforms via RPC, don't the sensors (and their transforms) already exist on the client?
     
  8. ErezShahaf

    ErezShahaf

    Joined:
    Feb 3, 2022
    Posts:
    70
    Let me explain, I had to assign each controller to the right player - and although I have all the sensors I had no idea who they belong to, so I planned to make a script that scans the players and send back their controllers to the client (which did not work). I ended up having to get the server to assign the ownership of the controllers of all the players and afterwards I had to call a functions on the client that scans all the players and tries to look for player and controllers with the same ID.