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

How to sync player position?

Discussion in 'Multiplayer' started by ultraviol3nt, Jun 8, 2013.

  1. ultraviol3nt

    ultraviol3nt

    Joined:
    Jan 17, 2010
    Posts:
    155
    Hi all.

    Not the greatest with networking, but here goes:

    The connection is good. Server can be created, and clients can connect. Player 1 starts server and spawns. Player 2 connects to server and spawns. Player 1 can see player 2 spawn, but cannot see player 2 move at all. Player 2 also cannot see player 1 at all.

    So I guess I need to get the NetworkViews to sync their data with the server. The code I have is from the M2H tutorials, but I'm not doing something right, obviously..

    Code (csharp):
    1. @RPC
    2. function OnSerializeNetworkView(stream : BitStream, info : NetworkMessageInfo)
    3. {
    4.     if (stream.isWriting){
    5.         //This is executed on the owner of the networkview
    6.         //The owner sends it's position over the network
    7.        
    8.         var pos : Vector3 = transform.position;    
    9.         stream.Serialize(pos);//"Encode" it, and send it
    10.                
    11.     }else{
    12.         //Executed on all non-owners
    13.         //This client receive a position and set the object to it
    14.        
    15.         var posReceive : Vector3 = Vector3.zero;
    16.         stream.Serialize(posReceive); //"Decode" it and receive it
    17.        
    18.         //We've just recieved the current servers position of this object in 'posReceive'.
    19.        
    20.         transform.position = posReceive;       
    21.         //To reduce laggy movement a bit you could comment the line above and use position lerping below instead:  
    22.         //transform.position = Vector3.Lerp(transform.position, posReceive, 0.9); //"lerp" to the posReceive by 90%
    23.        
    24.     }
    25. }
    So how do I get the player positions, rotations, and other info to sync?
     
    Last edited: Jun 8, 2013
  2. DryTear

    DryTear

    Joined:
    Nov 30, 2012
    Posts:
    312
    C# - use this script, its similar to M2H Leepos script, but a little different :

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. /// <summary>
    6. /// This script is attached to the player and it
    7. /// ensures that every players position, rotation, and scale,
    8. /// are kept up to date across the network.
    9. ///
    10. /// This script is closely based on a script written by M2H.
    11. /// </summary>
    12.  
    13.  
    14. public class PlayerSync : MonoBehaviour {
    15.    
    16.     private Vector3 lastPosition;
    17.    
    18.     private Quaternion lastRotation;
    19.    
    20.     private Transform myTransform;
    21.    
    22.     // Use this for initialization
    23.     void Start ()
    24.     {
    25.         if(networkView.isMine == true)
    26.         {
    27.             myTransform = transform;
    28.            
    29.            
    30.             //Ensure taht everyone sees the player at the correct location
    31.             //the moment they spawn.
    32.            
    33.             networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
    34.         }
    35.         else
    36.         {
    37.             enabled = false;   
    38.         }
    39.     }
    40.    
    41.     // Update is called once per frame
    42.     void Update ()
    43.     {
    44.         //If the player has moved at all then fire off an RPC to update the players
    45.         //position and rotation across the network.
    46.        
    47.         if(Vector3.Distance(myTransform.position, lastPosition) >= 0.1)
    48.         {
    49.             //Capture the player's position before the RPC is fired off and use this
    50.             //to determine if the player has moved in the if statement above.
    51.            
    52.             lastPosition = myTransform.position;
    53.             networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
    54.         }
    55.        
    56.         if(Quaternion.Angle(myTransform.rotation, lastRotation) >= 1)
    57.         {
    58.             //Capture the player's rotation before the RPC is fired off and use this
    59.             //to determine if the player has turned in the if statement above. 
    60.            
    61.             lastRotation = myTransform.rotation;   
    62.             networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
    63.         }
    64.     }
    65.    
    66.     [RPC]
    67.     void UpdateMovement (Vector3 newPosition, Quaternion newRotation)
    68.     {
    69.         transform.position = newPosition;
    70.         transform.rotation = newRotation;
    71.     }
    72. }
    73.  
    Attach to a player prefab (works on rigidbodies too), then have a network view, observing nothing and not sending any data (leave both fields set to None), then, thats it.

    This script updates scale (incase u got power ups), rotation and position
     
  3. ultraviol3nt

    ultraviol3nt

    Joined:
    Jan 17, 2010
    Posts:
    155
    You're my hero for the month :D

    Now what about syncing other variables, like health, etc?
     
  4. BFGames

    BFGames

    Joined:
    Oct 2, 2012
    Posts:
    1,543
    I would not recommend using RPC's for syncing the player position and rotation.

    What you do is correct, however it is NOT an RPC so remove the @RPC on top of the method.
     
  5. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    When the server is authoritative, there isn't another way.
    Don't give false advice.

    Wrong, it is an RPC call:
    Code (csharp):
    1. networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
     
    Last edited: Jun 9, 2013
  6. ultraviol3nt

    ultraviol3nt

    Joined:
    Jan 17, 2010
    Posts:
    155
    So "networkView.RPC("function")" is the function for syncing the variables across the network?
     
  7. Glynn-Taylor

    Glynn-Taylor

    Joined:
    May 14, 2013
    Posts:
    12
    Kinda, it's more like the ability to call methods (e.g. functions) on the same "instance" of the script on different machines on the network with a number of different parameters.
    E.g it can be used to set data on every machine to the servers data as seen above, but it could also be used to start a victory animation on every machine, or enable a rain particle emitter etc.
    It is essentially a way of running a function across all machines on a network.
    I feel you should be made aware that the RPCMode is what determines what machines receive the call to run the method and how this call is made.
    E.g. RPCMode.Server sends it only to the server machine, whilst RPCMode.Others sends it to all machines that aren't the machine that is making the RPC call.
    To see what the difference between buffered and non buffered RPC calls, see the bottom of this page: http://docs.unity3d.com/Documentation/Components/net-RPCDetails.html
    Keep in mind that you can remove buffered rpc calls that a machine has sent, e.g. when they disconnect this might be useful.

    EDIT for further info:
    Typically try to let the server do most of the work; e.g unless health needs to be displayed on client, mess with it on the server and when something dies, then let the client know.
    I'm not a networking expert, but if you have a variable that needs to be displayed constantly (like health), but is calculated on the server, you could send it by RPC, but limit the amount of RPC calls per second. For example is it really necessary that 60 updates are made per second to health?, you'd probably get away with 1/2 a second to be cheeky, but something like 1/10 -1/20 would probably be safer.
    Would be nice to get a professional opinion on this.
    I'm also unsure on any overhead that may occur when using RPC calls, I am presuming it is the same expense of using TCP sockets and streams, which can typically send/recieve data frequently and in decent amounts with no trouble at all.
     
    Last edited: Jun 9, 2013
  8. ultraviol3nt

    ultraviol3nt

    Joined:
    Jan 17, 2010
    Posts:
    155
    Thank you, you've all been very helpful :D

    Also, to help myself better understand what the code does, I've taken the time to convert DryTear's code to JavaScript:

    Code (csharp):
    1. #pragma strict
    2.  
    3. private var lastPosition : Vector3;
    4. private var lastRotation : Quaternion;
    5. private var myTransform : Transform;
    6.  
    7. function Start () {
    8.     if(networkView.isMine == true){
    9.         myTransform = transform;
    10.         networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
    11.     }
    12.     else{
    13.         enabled = false;
    14.     }
    15. }
    16.  
    17. function Update () {
    18.     if(Vector3.Distance(myTransform.position, lastPosition) >= 0.1){
    19.         lastPosition = myTransform.position;
    20.         networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
    21.     }
    22.     if(Quaternion.Angle(myTransform.rotation, lastRotation) >= 1){
    23.         lastRotation = myTransform.rotation;
    24.         networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
    25.     }
    26. }
    27.  
    28. @RPC
    29. function UpdateMovement (newPosition : Vector3, newRotation : Quaternion){
    30.     transform.position = newPosition;
    31.     transform.rotation = newRotation;
    32. }
    In converting, I've found the cause of a small issue I've been issue I've had. After player 2 spawns, he can't see player 1 until player 1 moves or turns, because of the conditions in the Update() function. Recommend invoking the RPC call at about the same interval recommended by Glynn Taylor, so that the player doesn't have to move or rotate to make the RPC.
     
    Last edited: Jun 9, 2013
  9. Glynn-Taylor

    Glynn-Taylor

    Joined:
    May 14, 2013
    Posts:
    12
    I'm presuming that you know about network view tracking of transform and animation etc, and are opting to take this route to prevent cheating etc.
    Movement may require faster/more updates to make it appear more fluid instead of jumpy; also my main reason for this post is that if you find that your movement is still jumpy regardless of send rate, you will either need to increase the Network.sendrate, or the more "professional"/safer way to do it is to make your characters interpolate/extrapolate/roughly move between updates in the vagueish direction of where they were going.
    I only have used these fixes in games using the transform tracking part of network view, and not by RPC calls, it is probable that RPC calls do not have a fixed send rate, and so you wont have this problem; though you may still be able to reduce the amount of information you are sending further, by having larger intervals between RPC calls, and interpolating player position.
    Just a few thoughts if you're concerned about network performance, a large amount of information on optimisation can be found here:https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking Though honestly if small scale you probably won't need to optimise it that much/at all, is just better practice too.

    Glad to hear you're having success; I feel like networking is a very fun topic, just is a bit hard to truly know the best ways of doing things, since no-one seems to be sure ^^
     
  10. DryTear

    DryTear

    Joined:
    Nov 30, 2012
    Posts:
    312
    if you're not comfortable with a C# booger being alone with Java, you can just attach a NetworkView onto your player prefab to send data accross network, so everyone else will recieve the data they want to recieve
     
  11. DryTear

    DryTear

    Joined:
    Nov 30, 2012
    Posts:
    312
    for that, use rpc calls to update that accross the network, so that others will have healths updated with rpc's. Also use RPCMode.AllBuffered, so that everyone, and joining players get info
     
  12. BFGames

    BFGames

    Joined:
    Oct 2, 2012
    Posts:
    1,543
    I was not refering to UpdateMovement, but hes OnSerializedNetwork() method...
     
  13. BFGames

    BFGames

    Joined:
    Oct 2, 2012
    Posts:
    1,543
    As he states that the player starts the server it does not seem much like an authoritative setup.
     
  14. ultraviol3nt

    ultraviol3nt

    Joined:
    Jan 17, 2010
    Posts:
    155
    Server is authoritative period. I just didn't work it correctly.
     
  15. ultraviol3nt

    ultraviol3nt

    Joined:
    Jan 17, 2010
    Posts:
    155
    So theoretically, this should work:

    Code (csharp):
    1.  
    2. var smooth : float;
    3.  
    4. function UpdateMovement (newPosition : Vector3, newRotation : Quaternion){
    5.     transform.position = Mathf.Lerp(transform.position, newPosition, (Time.time * smooth));
    6.     transform.rotation = Mathf.Lerp(transform.rotation, newRotation, (Time.time * smooth));
    7. }
    8.  
     
  16. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    Yes, your right on that one, but you should have specified it clearly.
    Other people can also refer to this post and make the same conclusion I made.
     
  17. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    TIP: don't assume things... you can't see by his posts if it is or not.
     
  18. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    To clarify:

    Code (csharp):
    1. @RPC
    2. function OnSerializeNetworkView(stream : BitStream, info : NetworkMessageInfo)
    3. {
    OnSerializeNetworkView does not need the to be tagged as an RPC.
     
  19. Glynn-Taylor

    Glynn-Taylor

    Joined:
    May 14, 2013
    Posts:
    12
    Probably not; firstly because the third argument of .Lerp needs to be a value between 0 and 1, so Time.time times by a constant will only work for the first 1/k seconds of the game; see this answer: http://webcache.googleusercontent.c...mathflerp-work.html+&cd=2&hl=en&ct=clnk&gl=uk
    Also I think .Lerp needs to be called constantly to have an effect, rather than just once which is what would happen if that was an rpc call or something. Perhaps a more easier/simpler approach would simply be to add the transform.forward to the transform as that would be the direction of motion from the last update.
    I feel like the discussion on this thread surrounding the labelling of RPC in the initial code is unnecessary, seeing as the author has stated that it was tutorial code and has changed his approach since.
     
  20. BFGames

    BFGames

    Joined:
    Oct 2, 2012
    Posts:
    1,543
    As stated then Lerp needs to run over several frames and not a single as it goes from one position to another over time. Cause lets say you send out position and rotation 40 times per second, but the player plays at 60 FPS, then it will seem laggy.

    What you can do is saving them as variables in UpdateMovement(), and then use some sort of interpolation in update or fixed update depending on your game.

    Furthermore mathf.Lerp is for single floating points, you need some kind of interpolation for Vector3 and Quaternions, like Vector3.MoveTowards, Vector3.Lerp and Quaternion.Lerp.

    Code (csharp):
    1.  
    2. var smooth : float;
    3. var newPos : Vector3;
    4. var newRot : Quaternion;
    5.  
    6. function UpdateMovement (newPosition : Vector3, newRotation : Quaternion)
    7. {
    8.     //save variables here
    9.     newPos = newPosition;
    10.     newRot = newRotation
    11. }
    12.  
    13. function Update()
    14. {
    15.     //Now interpolate to the newest position and rotation
    16.       transform.position = Vector3.Lerp(transform.position, newPos, (Time.deltaTime * smooth));
    17.        transform.rotation = Quaternion.Lerp(transform.rotation, newRot, (Time.deltaTime * smooth));
    18. }
    19.  
     
    Last edited: Jun 10, 2013
  21. BFGames

    BFGames

    Joined:
    Oct 2, 2012
    Posts:
    1,543
    Well sorry i misunderstood it then.

    But when you say that "Player 1 starts server and spawns." - Then you have a player controlling the game server and not a server (without anyone playing in that instance) handling all movement it seems.
     
    Last edited: Jun 10, 2013
  22. Mint92

    Mint92

    Joined:
    Nov 4, 2013
    Posts:
    1
    Im not sure where im going with my code, i need update the turret on the client side, it works locally, but im not sure where im going wrong
    void Update ()
    {
    if (game.running Network.player == owner)
    {
    playerActive = true;

    bool shootButton;



    //Move the player left and right using the arrow keys
    if(Input.GetButtonDown("Right"))
    {
    transform.position = new Vector3(transform.position.x + 1.0f, transform.position.y,transform.position.z);
    }
    else if(Input.GetButtonDown("Left"))
    {
    transform.position = new Vector3(transform.position.x -1.0f, transform.position.y,transform.position.z);
    }




    //Call SetPosition to notifty the client is moving
    if(Vector3.Distance(transform.position,lastPosition) <= 1)
    {
    networkView.RPC("Log", RPCMode.Server, "Client " + owner + " is moving");

    lastPosition = transform.position;
    networkView.RPC("SetPosition", RPCMode.Others, gameObject.name, transform.position);
    }


    if(Quaternion.Angle(transform.rotation,lastRotation)>= -10)
    {
    networkView.RPC("Log", RPCMode.Server, "Client " + owner + " is rotating");
    lastRotation = transform.rotation;
    networkView.RPC("SetRotation", RPCMode.Others, gameObject.name,transform.rotation);
    }
    }

    [RPC]
    public void SetPosition(string shipName, Vector3 newPosition)
    {
    // find the local version of the ship
    GameObject ship = GameObject.Find(shipName);

    networkView.RPC("Log", RPCMode.Server, "Updating position of " + ship.name + " to " + newPosition);
    ship.transform.position = newPosition;
    }


    [RPC]
    public void SetRotation(string TurretPivotControl, Quaternion newRotation)
    {
    //find the local version of the turretpivotControl
    GameObject turret = GameObject.Find("TurretPivotControl");
    networkView.RPC("Log", RPCMode.Server, "Updating rotation of " + turret.name + " to " + newRotation);
    turret.transform.rotation= newRotation;
    }