Which are the best settings to sync position & rotation ?

Hi,
Im trying to up a cars game with photon, but I have still many troubles with that synchronizing.
I had try many scripts but that's not really good synced.
I'm wondering about PhotonNetwork.sendRate & PhotonNetwork.sendRateOnSerialize
Do you think that leave the default settings is fine or I must change these values ?

Best Answers

  • [Deleted User]
    Answer ✓
    Conclusion : All the time, positions of the players are late and something there are lagging ...


    This is 'normal' behaviour due to lag / latency, but you have options to deal with them. Therefore I would recommend you taking a look at my comment here. It is basically a hint for taking a look at the PUN2 Beta package which has a new demo including something we call Lag Compensation in order to get mostly rid of such issues. There will be also a new documentation page about this topic in the next days / weeks - no ETA about this one.

    If you have further questions, please feel free to ask.

Answers

  • Hi @M4TT,

    increasing the sendRate doesn't automatically fixes any occurring synchronization issues. Also increasing the value too much could end in even worse results than before.

    What kind of synchronization issues do you have? Are synchronized objects stuttering in at least one client's view? Are there a lot of position offsets between clients' views? Please share us some more details about this.
  • Thank you @Simon.
    Ofc that's it, I can see each others players stuttering a bit, and they are late of their real position.
    I was wondering about the true sync system but it's outdated now...

    To be more precise, my player move every frame called by the Update function.
    I tried to sync his transform by the lerp effect on the Transform photon view component and by a simple script which is :
        private void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
        {
            if (stream.isWriting)
            {
                stream.SendNext(ship.transform.position);
                stream.SendNext(ship.transform.rotation);
            }
            else
            {
                TargetPosition = (Vector3)stream.ReceiveNext();
                TargetRotation = (Quaternion)stream.ReceiveNext();
            }
        }
    + this one on the Update funct if photonView is not mine :
        private void SyncMove()
        {
            ship.transform.position = Vector3.Lerp(ship.transform.position, TargetPosition, 0.5f);
            ship.transform.rotation = Quaternion.RotateTowards(ship.transform.rotation, TargetRotation, 720f * Time.deltaTime);
        } 
    I tried this script which I found on the net : http://gamieon.com/blogimages/devblog/20161010/GamieonTransformSync.cs
    And I tried the script of the Paladins studio tutorial about photon.
    Conclusion : All the time, positions of the players are late and something there are lagging ...
    Could you help please
  • [Deleted User]
    Answer ✓
    Conclusion : All the time, positions of the players are late and something there are lagging ...


    This is 'normal' behaviour due to lag / latency, but you have options to deal with them. Therefore I would recommend you taking a look at my comment here. It is basically a hint for taking a look at the PUN2 Beta package which has a new demo including something we call Lag Compensation in order to get mostly rid of such issues. There will be also a new documentation page about this topic in the next days / weeks - no ETA about this one.

    If you have further questions, please feel free to ask.
  • Thank you ill take a look.
    Will the new topic be about PUN2 " synchronization " ?
  • I'm looking for the script of the asteroid & the spaceship in the PUN2 package and as I can see, it works with the velocity.
    Currently my players are moving through transform.translate instead of rigidbody.velocity, I dont know if I can do something in this case ?
  • Yes, in the demo we are working with the Rigidbody component. The velocity value describes the movement of the object (direction and speed). Based on the current and the previous position of the transform component you should be able to 'create' the necessary information yourself and send this value in the OnPhotonSerializeView, too. I have not tested this behaviour, but I'm pretty sure that this will work as well.
  • There is my script, can you tell me if I'm right ?
    
    void Update(){
                    destination += (transform.forward * currentSpeed * Time.deltaTime);
                    ship.transform.position = Vector3.Lerp(ship.transform.position, destination, 0.5f);
    }
        public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
        {
            if (stream.isWriting)
            {
                stream.SendNext(ship.position);
                stream.SendNext(ship.rotation);
                stream.SendNext(currentSpeed); //Float value, actual speed.
            }
            else
            {
                rigidbody.position = (Vector3)stream.ReceiveNext();
                rigidbody.rotation = (Quaternion)stream.ReceiveNext();
                currentSpeed = (float)stream.ReceiveNext();
    
                float lag = (float)(PhotonNetwork.time - info.timestamp);
                ship.position += currentSpeed * lag;
            }
        }
  • I think there are some issues in your code. I'm starting with the OnPhotonSerializeView implementation, especially with the 'receiving' side, where you apply updates directly to the Rigidbody component. This is basically okay, but I think you don't have to do this. Instead try storing those updates in local variables and apply them later. The problem is that you are updating the ship's position at this point, which might result in stuttering movement. The better visual result in my opinion is - as said - to store the calculated position in a local variable and update it in the Update function, as seen in the Spaceship class from the PUN2 Asteroids demo.

    Some words on the Update function you already have: I don't know if it is working this way (usage of destination variable) so you have to try and figure that out. What I have tried in the meantime is to store the transform.position value in a local variable ('oldPosition'), processing all input and calculating the 'movement', which is transform.position (updated) - oldPosition. The sender then uses this value as third value. On the receiving side it is more or less the same as shown in the Asteroids demo.

    Some other hints to the Update function: make sure to use the PhotonView.isMine condition to avoid input from remote clients for example. Again I recommend taking a look at the implementation from the Asteroids demo, you can re-use a lot of its code in my opinion.
  • Ok thank you !
    I will learn about the old & current position.
    Can I try my script on PUN or I msut try it on PUN2 ? ( Do it should be the same render ? )
  • M4TT
    M4TT
    edited February 2018
    Edit² : After reflexion I think that the most simple way to sync player based on Pun2 script is to go through that RigidBody.velocity instead transform.Translate, I'll try to set up my code and I ll see if it's good.
    All the text below is about a method to convert a float value to a Vector3, it's relatively off topic, do not pay attention

    I'm trying to convert my float value which represent the current speed of the gameObject to a Vector3.
    Then I can send this value over the network based on the SpaceShip script, I currently got this :
    ( I know it's a bit too long, but I allow myself to put it " in full " )

       
    // <b class="Bold">That script below doesnt works</b>
        private Vector3 velocity;
        private Vector3 prevPos;
    
        private Vector3 velVector;  //the var which should return the velocity
    
        private Vector3 _netPos;
        private Quaternion _netRot;
    
        private void Start()
        {
            if ( !Photonview.isMine )
            {
                _netPos = ship.transform.position;
                _netRot = ship.transform.rotation;
            }
        }
    
        private void Update()
        {
            if (Photonview.isMine)
            {
                    destination += (transform.forward * currentSpeed * Time.deltaTime);
                    ship.transform.position = Vector3.Lerp(ship.transform.position, destination, 0.5f); //local movement of the gameObject.
    
                    float fwdDot = Vector3.Dot(transform.forward, velocity);
                    velVector = new Vector3(0, 0, fwdDot);
            }
        }
        private void FixedUpdate()
        {
                if (!photonView.isMine)
                {
                    ship.transform.position = Vector3.MoveTowards(ship.transform.position, _netPos, Time.fixedDeltaTime);
                    ship.transform.rotation = Quaternion.RotateTowards(ship.transform.rotation, _netRot, Time.fixedDeltaTime * 720);
                    return;
                }
                else
                {
                    velocity = (ship.transform.position - prevPos) / Time.deltaTime;
                    prevPos = ship.transform.position;
                }
        }
    
    //This is the script which is availaible on Pun2, I just replaced the velocity by my own velocity var.
        public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
        {
            if (stream.isWriting)
            {
                stream.SendNext(ship.transform.position);
                stream.SendNext(ship.transform.rotation);
                stream.SendNext(velVector);
            }
            else
            {
                _netPos = (Vector3)stream.ReceiveNext();
                _netRot = (Quaternion)stream.ReceiveNext();
                velVector = (Vector3)stream.ReceiveNext();
    
                float lag = Mathf.Abs((float)(PhotonNetwork.time - info.timestamp));
                _netPos += (velVector * lag);
    
                if (Vector3.Distance(ship.transform.position, _netPos) > 20.0f) 
                {
                    ship.transform.position = _netPos;
                }
    
            }
        }

    I just wanted to know your opinion if possible, ill try this script tonight or tommorov.
    Thanks!

    Edit :
    As I can see that just doesn't works, I tried to put my script in relation to your method :
        private void Update()
        {
            if (Photonview.isMine)
            {
                if (!isPlayersWaiting && isPlayerAlive)
                {
                    var oldPos = ship.transform.position;
                    destination += (transform.forward * currentSpeed * Time.deltaTime);
                    ship.transform.position = Vector3.Lerp(ship.transform.position, destination, 0.5f);
                    velocity = new Vector3(0, 0, ship.transform.position.z - oldPos.z);
                }
            }
    
        public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
        {
            if (stream.isWriting)
            {
                stream.SendNext(ship.transform.position);
                stream.SendNext(ship.transform.rotation);
                stream.SendNext(velocity);
    
            }
            else
            {
                ship.transform.position = (Vector3)stream.ReceiveNext();
                ship.transform.rotation = (Quaternion)stream.ReceiveNext();
                velocity = (Vector3)stream.ReceiveNext();
    
                float lag = Mathf.Abs((float)(PhotonNetwork.time - info.timestamp));
                ship.transform.position += (velocity * lag);
                print(_netPos);
                print(velocity);
                print(PhotonNetwork.time - info.timestamp);
    
            }
        }

  • Hello, I have still trouble with that sync.
    I just followed the Spaceship script, and as I can see, the position of the players are very nice synced, that seems beautifull, but it's jerky a lot :[ :
        private void Start()
        {
            if(!Photonview.isMine)
            {
                _netPos = transform.position;
                _netRot = transform.rotation;
            }
        }
    
        private void FixedUpdate()
        {
            if (!Photonview.isMine)
            {
                ship.transform.position = Vector3.MoveTowards(ship.transform.position, _netPos, Time.fixedDeltaTime); // I wonder if I need to change this line with Vector3.Lerp ??
                ship.transform.rotation = Quaternion.RotateTowards(ship.transform.rotation, _netRot, Time.fixedDeltaTime * 720.0f);
                return;
            }
        }
    
        public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
        {
            if (stream.isWriting)
            {
                stream.SendNext(ship.transform.position);
                stream.SendNext(ship.transform.rotation);
                stream.SendNext(Rb.velocity);
    
            }
            else
            {
                _netPos = (Vector3)stream.ReceiveNext();
                _netRot = (Quaternion)stream.ReceiveNext();
                Rb.velocity = (Vector3)stream.ReceiveNext();
    
                float lag = Mathf.Abs((float)(PhotonNetwork.time - info.timestamp));
                print(lag);
                _netPos += (Rb.velocity * lag);
                if (Vector3.Distance(ship.transform.position, _netPos) > 20.0f) // more or less a replacement for CheckExitScreen function on remote clients
                {
                    ship.transform.position = _netPos;
                }
    
            }
        }
    By the way I changed the PhotonNetwork send rate value to default.
    Any idea ?
  • I wonder if I need to change this line with Vector3.Lerp ??


    You can try this and see, if the result looks more satisfying for you. I guess you can also remove the replacement for the CheckExitScreen function if you don't need it. In your FixedUpdate function you are updating the Transform component of the object. since you are already using the Rigidbody component, you can also try updating the Rigidbody instead of the Transform component. Besides that I think this looks fine so far.

    For more information you can also take a look at the new documentation page which is available now. It covers both approaches for using objects with and without Rigidbody components.
  • M4TT
    M4TT
    edited February 2018
    Ok thank you, ill try to resolve this issue.
    To be sure, Can I try this script on PUN1 ?
    Because I'm wondering why that spaceship & asteroid script are only on PUN2 ?
    Edit : Lerp solved my issue, there is a llittle bit offset on each player position but it's normal from that lerp.
    Thank you ! <3
  • M4TT
    M4TT
    edited February 2018
    Following your help and the resolution of my sync position issue, this has generated an another one ^^.
    I'm not creating an another thread because that issue is related to the previous.
    I am not in a hurry and ill try to explain this issue, I hope that someone could help me =)

    When players are moving they generate a gameobject behind them every 0.3 sec.
    In my Update() function there is the followinf code :
                   
     if (0.0 >= nextWall) // if it's time to generate the gameObject
    {
       nextWall = 0.3f; //set countdown for the next gameObject
    
       GameObject collider;
       collider = Instantiate(Enemy, Rb.position + Rb.transform.forward * distance, Rb.rotation);//instantiate the gameObject just behind the rigidbody component on the local player
    
       Photonview.RPC("SpawnCollider", PhotonTargets.Others);// the local player generate his gameObject over the network for the others players
    }
    
    if (nextWall > 0.0f) // if the generation of the gameObject is reloading 
    {
       nextWall -= Time.deltaTime; // we wait a delay and the next gameObject can be generated
    }
    
    [PunRPC]
    private void SpawnCollider()
    {
       GameObject collider;
       collider = Instantiate(Enemy, Rb.position + Rb.transform.forward * distance, Rb.rotation);
    }
    
    Well, the issue is like the previous one, the gameObject is created over the network but it's only synced on the local side.
    I also tried alternative but I just lost about ten hours ^^. ( for example try to add compensation from lag : nexWall = 0.3f - netLag, I tried througt allViaServer method ... )
    I just made a picture to illustrate the situation :
    https://www.noelshack.com/2018-06-4-1518044851-bmj.jpg
  • Have you already tried sending the position of the new game object with the RPC call itself? This way you would make sure that the object has the same position on all clients. For example: Photonview.RPC("SpawnCollider", PhotonTargets.Others, position);

    The RPC handler then looks like this: private void SpawnCollider(Vector3 position) { }
  • M4TT
    M4TT
    edited February 2018
    It seems to me that I had already tested, but ill try to be sure.
    Edit : that just perfect, thanks a lot @Christian_Simon !
        Photonview.RPC("_SpawnCollider", PhotonTargets.Others, Rb.position, Rb.rotation);
        [PunRPC]
        private void _SpawnCollider(Vector3 position, Quaternion rotation)
        {
            GameObject collider;
            collider = Instantiate(Enemy, position + Rb.transform.forward * distance, rotation);
    
        }
  • Just for telling that my world scale was too bit, the the sync was right but I remade a new space with a bigger scale like * 70 - 100 and now I can use that Vector3.MoveTowards, it's not jerky anymore and player moves are more accurate.
  • Hapy
    Hapy
    edited July 2021
    
    Hello, I could use some help to synchronize my Characters rotation.
    The character has a (Rigidbody, Capsule Collider, GroundFitter Components, Photon View, Photon Rigidbody View, Photon Transform View, Photon Animator View and the script I created Photon Network Player) attatched too it.
    PS: The character becomes a child object of an empty GameObject called "Player", when I connect and start the Game.
    The Player has a (Photon View and Photon Transform View) attatched.

    This is the PhotonNetworkPlayer script:


    private Transform character;
        private Transform personalCamera;
        private FGroundFitter_Demo_JoystickInput joystickInput;
        private SingleAttack singleAttack;
        private AnimalTigerRoar animalRoar;
    
        private Rigidbody r;
    
        //For smoothing the Movement of other Characters
        Vector3 latestPos;
        Quaternion latestRot;
        Vector3 velocity;
        Vector3 angularVelocity;
    
        bool valuesReceived = false;
    
        //Link the Animator
        private Animator animator;
    
    
         private void Start()
        {
            if (photonView.IsMine)
            {
                GetComponent<FGroundFitter_Demo_JoystickInput>().enabled = true;
                GetComponent<SingleAttack>().enabled = true;
                GetComponent<AnimalTigerRoar>().enabled = true;
                personalCamera.gameObject.SetActive(true);
            }
            else
            {
                Destroy(character);
                Destroy(this);
            }
        }
    
         private void FixedUpdate()
        {
            GetNeededComponents();
        }
    
         private void GetNeededComponents()
        {
            GameObject temporaryCamera = GameObject.FindGameObjectWithTag("Camera");
            personalCamera = temporaryCamera.transform.GetChild(0);
    
            GameObject temporaryPlayer = GameObject.FindGameObjectWithTag("Player");
            character = temporaryPlayer.transform.GetChild(0);
    
            joystickInput = GetComponent<FGroundFitter_Demo_JoystickInput>();
    
            singleAttack = GetComponent<SingleAttack>();
    
            animalRoar = GetComponent<AnimalTigerRoar>();
    
            animator = GetComponent<Animator>();
    
            r = GetComponent<Rigidbody>();
        }
    
    
        //Send the Character Position and Rotation through the Server
         public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
        {
            if (stream.IsWriting)
            {
                // We own this player: send the others our data
                stream.SendNext(transform.position);
                stream.SendNext(transform.rotation);
                stream.SendNext(r.velocity);
                stream.SendNext(r.angularVelocity);
                stream.SendNext(animator.GetBool("Walk"));
                stream.SendNext(animator.GetBool("Run"));
                stream.SendNext(animator.GetBool("Sound"));
                stream.SendNext(animator.GetBool("Attack"));
            }
            else
            {
                // Network player, receive data
                latestPos = (Vector3)stream.ReceiveNext();
                latestRot = (Quaternion)stream.ReceiveNext();
                velocity = (Vector3)stream.ReceiveNext();
                angularVelocity = (Vector3)stream.ReceiveNext();
                animator.SetBool("Walk", (bool)stream.ReceiveNext());
                animator.SetBool("Run", (bool)stream.ReceiveNext());
                animator.SetBool("Sound", (bool)stream.ReceiveNext());
                animator.SetBool("Attack", (bool)stream.ReceiveNext());
    
                valuesReceived = true;
            }
        }
    
       private void Update()
        {
            if (!photonView.IsMine && valuesReceived)
            {
                //Update Object position and Rigidbody parameters
                transform.position = Vector3.Lerp(transform.position, latestPos, Time.deltaTime * 5);
                transform.rotation = Quaternion.Lerp(transform.rotation, latestRot, Time.deltaTime * 5);
                r.velocity = velocity;
                r.angularVelocity = angularVelocity;
            }
        }
    
        void OnCollisionEnter(Collision contact)
        {
            if (!photonView.IsMine)
            {
                Transform collisionObjectRoot = contact.transform.root;
                if (collisionObjectRoot.CompareTag("MeshPlayer"))
                {
                    //Transfer PhotonView of Rigidbody to our local player
                    photonView.TransferOwnership(PhotonNetwork.LocalPlayer);
                }
            }
        }