Search Unity

[SOLVED] How would I use SyncVars to transmit transform data to all clients?

Discussion in 'Multiplayer' started by BluetoothBoy, Jun 22, 2015.

  1. BluetoothBoy

    BluetoothBoy

    Joined:
    Jun 17, 2015
    Posts:
    14
    I currently have a multiplayer LAN setup that currently only allows other clients to connect and move their character around. The character (a car) uses Unity's vehicle physics (wheel colliders, rigidbodies, etc.) All of that works fine with a NetworkTransform on the vehicle with sync set to Rigidbody3D. However, as is to be expected, the wheel meshes for other player's cars (which are children of the main player object) do not update by themselves, which I must say looks rather silly for a car. At the moment, I'm just using a modified version of the demo car's script, and I've tried using both RPCs and Commands to get the position and rotation to update on the wheels. I feel like this has a very simple solution to it, but I can't figure it out.

    Here is the relevant code:
    Code (CSharp):
    1. [Command]
    2. public void CmdUpdateWheels(Vector3 pos, Quaternion rot, int wheel)
    3. {
    4.         m_WheelMeshes[wheel].transform.position = pos;
    5.         m_WheelMeshes[wheel].transform.rotation = rot;
    6. }
    This is called in a for loop in the Move function, like so:
    Code (CSharp):
    1. CmdUpdateWheels(position, quat, i);
    I also tried doing this with an RPC as well. I have also attempted this with the class as a Monobehaviour and a NetworkBehaviour.
     
  2. Carpe-Denius

    Carpe-Denius

    Joined:
    May 17, 2013
    Posts:
    842
    Just syncronize the cars speed, so that every client updates it's own wheels. Thats just a float and not 28 to syncronize.

    I don't know if it is fixed yet, but you should put your wheel update code in Update(), otherwise your wheels will stand still if you don't accelerate, even if the car moves.
     
  3. BluetoothBoy

    BluetoothBoy

    Joined:
    Jun 17, 2015
    Posts:
    14
    Each client already does update its own wheels - that's not the problem. The wheels' individual transforms on the other players' cars don't update on the client side. Also, Move is called every frame, so that is a non-issue.
     
  4. Carpe-Denius

    Carpe-Denius

    Joined:
    May 17, 2013
    Posts:
    842
    Thats why I said that you should sync a speedvar, so that each client can update the wheels locally. All of them, not just of one car. That way you have to transmit less data than if you transmit 4 transforms for every car.
     
  5. BluetoothBoy

    BluetoothBoy

    Joined:
    Jun 17, 2015
    Posts:
    14
    Oh, ok. Sorry - it seems I just didn't understand what you were saying originally. I will give that a try!

    For future reference, though, any idea what I was doing incorrectly with commands and RPCs? I wouldn't mind having a better grasp of those.
     
  6. BluetoothBoy

    BluetoothBoy

    Joined:
    Jun 17, 2015
    Posts:
    14
    I believe at this point I have thoroughly confused myself. How do I go about making this code:

    Code (csharp):
    1.  
    2. for (int i = 0; i < 4; i++)
    3. {
    4.      Quaternion quat;
    5.  
    6.       Vector3 position;
    7.  
    8.       m_WheelColliders[i].GetWorldPose(out position, out quat);
    9.       m_WheelMeshes[i].transform.position = position;
    10.       m_WheelMeshes[i].transform.rotation = quat;
    11. }
    12.  
    ...update on all clients using SyncVars? I understand them (mostly), but am having trouble working out how to implement them in this situation.

    In case you couldn't tell, I'm very new to networking.
     
  7. Carpe-Denius

    Carpe-Denius

    Joined:
    May 17, 2013
    Posts:
    842
    Somewhere else in the code the wheelcolliders get:
    -> a force forward
    -> 1 (sync 1 float per car)
    -> they push the car
    -> your Code (getWorldPose) gets the position of the Wheel
    -> you set that to your meshes
    -> 2 (sync one rotation and one position -> 7 floats per wheel)

    You try to sync "2", but I think it is easier to sync "1". Get the force, sync it to the server with a SyncVar and let every client calculate the position by itself. Since you have a NetworkTransform on your car, small differences in the calculations don't matter (aside from maybe a wheel, thats rotated some degrees different on different clients).

    If you want to work with networktransforms on every wheel, you could do that, but every WheelMesh needs to get assigned to your driver with NetworkServer.AddPlayerForConnection(yourDriverConnection, wheelGameObject, someIntAboveZero)
    If you do that, you have to live with a current bug, which should be fixed in the next patch. You can't remove the wheel from playerAuthority with ReplacePlayerForConnection() (bug) and sadly there is no RemovePlayerForConnection() for just one object, only for all (so you lose the authority for the car, your player etc. too)
     
  8. BluetoothBoy

    BluetoothBoy

    Joined:
    Jun 17, 2015
    Posts:
    14
    Ok, that makes sense. However, I am currently trying a slightly different method, an idea spawned from your suggestion. Instead of syncing a Vector3 and a Quaternion, I will simply sync the local X rotation and local Z position of each wheel. Not 1 float, sure, but I think 2 is still better than 7. :)
     
  9. Carpe-Denius

    Carpe-Denius

    Joined:
    May 17, 2013
    Posts:
    842
    You still need to add the wheels to the playerconnection, though.
    And it's one float per car vs. 2 floats per wheel ;)

    I don't know how many values a standard connection could handle, so I am a bit cautious not to send too much data. Maybe too much, but my connection handles 1MBit upload (sometimes 1.5...). With 30 updates per second thats around 4kb maximum or 146 uncompressed transforms (5 per car -> 29 cars instead of 128 cars) if you don't have any overhead (you have) and you don't send anything else (you will), but compression will probably help you a bit. I don't know the real length of a network message for transforms.
     
    Last edited: Jun 23, 2015
  10. BluetoothBoy

    BluetoothBoy

    Joined:
    Jun 17, 2015
    Posts:
    14
    Ah, misread that then. Any chance you can provide a bit of example code on how to go about implementing your method (both converting the wheel collider data to rotation and position and the network side of things as well)? I may have several years experience with the Unity engine, but I am at a loss around networking code (hopefully that will change soon!) and have very little experience with Unity's wheel colliders - they were, for some reason, something I always shied away from.

    Much obliged to you and all the help you have offered so far. :)

    EDIT: After doing all of one minute of research, I found the rpm variable of the wheel collider. I should be able to do the calculation part myself now (although I will still have to send four values for the Z position of the wheels. Still, now it's down from 8 to 5). If you can provide any assistance concerning the specifics of getting the networking side working, I'll be very grateful.
     
  11. BluetoothBoy

    BluetoothBoy

    Joined:
    Jun 17, 2015
    Posts:
    14
    So, I have now reached a point at which I have successfully made the wheels' rotation update for all clients without a hitch. CmdUpdateWheels is called if the player is a client only, which then calls the RPC; if it a a server with a player, just the RPC is called. However, I cannot seem to get the suspension position to work. Any ideas?

    Code (csharp):
    1.  
    2.         [Command]
    3.         public void CmdUpdateWheels(float rpm, float steer, float[] yPos)
    4.         {
    5.             RpcUpdateWheels(rpm, steer, yPos);
    6.         }
    7.  
    8.         [ClientRpc]
    9.         public void RpcUpdateWheels(float rpm, float steer, float[] yPos)
    10.         {
    11.             for(int i = 0; i < 4; i++)
    12.             {
    13.                 m_WheelMeshes[i].transform.RotateAround(m_WheelMeshes[i].transform.position, m_WheelMeshes[i].transform.right, rpm*2f*Time.deltaTime);
    14.                 if(i < 2)
    15.                     m_WheelMeshes[i].transform.parent.localEulerAngles = new Vector3(m_WheelMeshes[i].transform.parent.localEulerAngles.x, steer*1.5f, m_WheelMeshes[i].transform.parent.localEulerAngles.z);
    16.             }
    17.         }
    18.  
    Also, if someone can knows of a less data-intense method for doing this same exact thing, please let me know. At the moment, there is not visible latency, but who knows what the future holds...
     
  12. Carpe-Denius

    Carpe-Denius

    Joined:
    May 17, 2013
    Posts:
    842
    You don't need to calculate anything. Just sync the movespeed and let the local client handle the rest. I think the method is even called "Move". I will look into that later, I don't have the scripts here.
     
  13. BluetoothBoy

    BluetoothBoy

    Joined:
    Jun 17, 2015
    Posts:
    14
    Well, I now feel like a total idiot. I hosted a server in the editor and connected with a build, and had the brilliantly simple idea to check the connected client's vehicle. Turns out, the wheel colliders and their positions/rotations were present and working all along. So, I don't even need to transfer any of the data - it is all redundant. Instead, I'm just having each client attach a script locally to each player vehicle when they are spawned that handles the transforms. For some reason the steering doesn't come through, but I think that's because it is reliant solely on user input (which is client-side only). For that, I'm just sending a single float via SyncVar.