PUN 2 cannot fix laggy movement

I have setup up my char with this settings:

My code for for the networkPlayer is:

public class NetworkPlayer : MonoBehaviourPunCallbacks, IPunObservable
{

   
    private Vector3 networkPosition;
    private Quaternion networkRotation;
    private float smoothing = 10.0f;
 

    // Start is called before the first frame update
    void Start()
    {
        if(!GetComponent<PhotonView>().IsMine)
        {
            Destroy(GetComponent<SplineFollower>());
            tag = "OtherPlayer";
        }
     
        networkPosition = transform.position;
        networkRotation = transform.rotation;
    }

      void Update()
    {
        if (!photonView.IsMine)
        {
          
             transform.position = Vector3.Lerp(transform.position, networkPosition, Time.deltaTime * smoothing);
             transform.rotation = Quaternion.Lerp(transform.rotation, networkRotation, Time.deltaTime * smoothing);
        }
    }




    /// <summary>
    /// Used to customize synchronization of variables in a script watched by a photon network view.
    /// </summary>
    /// <param name="stream">The network bit stream.</param>
    /// <param name="info">The network message information.</param>
    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
      

       
        if (stream.IsWriting)
        {
            stream.SendNext(transform.position);
            stream.SendNext(transform.rotation);
        }
        else
        {
            networkPosition = (Vector3)stream.ReceiveNext();
            networkRotation = (Quaternion)stream.ReceiveNext();
        }
    }
}

i have tried changing the smooth factor, but without success, the movement is local horrible :frowning:

Here is a video with my problem:

I don’t know much about Photon so sorry if this isn’t useful, but I notice that you have a PhotonTransformView with synchronization settings for both position and rotation, but your script is also trying to update the position and rotation. Does the PhotonTransformView do that for you, or is what you are doing how it is supposed to work?

2 Likes

The screenshot is a bit confusing. I have tried both: code solution and photon script solution. the problem is the same.

You don’t have bittorrent or anything on in the background do you? And have you checked to make sure that you are connected to your actual region rather than one which might cause lag?

There is nothing in background, i also think that the code is ok, thats why i have no idea.
The LAG is local. Local you should not see this big lags.
The player moves with a fixed update on a spline (Dreamteck splines), but photon use update.
I have no idea to solve this. maybe i need to deactivate player scripts on remote side?
:frowning:

Try Vector3.MoveTowards / Quaternion.RotateTowards instead with high speed value.
Lerp doesn’t work in the way you’ve wrote it.

If you want a proper interpolation, there’s way more to tackle than you think.
You’d need to receive a time stamp from the server, and check against time stamp on the client to determine where that position should be in the current time frame. Then determine percentage between updates, and interpolate accordingly.

Before you start diving into this rabbit hole, make sure there’s no Photon interpolation available somewhere along those Photon scripts.

I haven’t used Photon, so you might probably want to ask on the Multiplayer sub-forum or specifically in the Photon thread instead.

1 Like

I thought it is much easier with photon :frowning:

MoveTowards make more LAG
I have tried this settings:
4729163--447863--upload_2019-7-9_19-24-30.png

Extrapolate on/off makes no difference and network script is:

public class NetworkPlayer : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        if(!GetComponent<PhotonView>().IsMine)
        {
            Destroy(GetComponent<SplineFollower>());
            tag = "OtherPlayer";
        }
    }

    // Update is called once per frame
    void Update()
    {
       
    }
}

The problem is the Photon forum is actually pretty inactive and there’s just barely anyone there to respond, I myself have lost temper with Photon quite a few times over stuff like this and let’s be real these guys aren’t doing their asset for free they’re looking to make money from it.

The unity forums is oddly the only place you can get proper responses for these types of questions. I really hope that Unity hurry up and upgrade to their latest UNet alternative, I want to see multiplayer unity with proper support. I’ve gotten a bit off topic but I came across this weird issue where I had the editor making my other play move but the other play would move individually absolutely fine which is weird and no mention of this on the forums or anywhere else.

I totally understand this guy’s frustration.

1 Like

I had the same yesterday, player moves without a reason.
I need for my game multiplayer, because otherwise the game is a bit senseless.
photon forum is dead, thats a bit idiotic with the photon forum, because i like to have a plan for 100 users, but without multiplayer support i dont need it.

thats the reason why i wrote here, photon forum did not answer.

Back to topic:
You mean something like this for a solution?

https://github.com/Landixus/Mobile-Multiplayer-Action-Game-in-Unity/blob/master/Underdog City/Assets/Scripts/Player/NetworkPlayer.cs

using Photon.Pun;
using UnityEngine;

namespace UnderdogCity
{

    public class NetworkPlayer : MonoBehaviourPun, IPunObservable
    {

        protected Player Player;
        protected Vector3 RemotePlayerPosition;
        protected float RemoteLookX;
        protected float RemoteLookZ;
        protected float LookXVel;
        protected float LookZVel;

        private void Awake()
        {
            Player = GetComponent<Player>();

            //destroy the controller if the player is not controlled by me
            if (!photonView.IsMine && GetComponent<Controller>() != null)
                Destroy(GetComponent<Controller>());
        }

        public void Update()
        {
            if (photonView.IsMine)
                return;

            var LagDistance = RemotePlayerPosition - transform.position;

            //High distance => sync is to much off => send to position
            if (LagDistance.magnitude > 5f)
            {
                transform.position = RemotePlayerPosition;
                LagDistance = Vector3.zero;
            }

            //ignore the y distance
            LagDistance.y = 0;

            if (LagDistance.magnitude < 0.11f)
            {
                //Player is nearly at the point
                Player.Input.RunX = 0;
                Player.Input.RunZ = 0;
            }
            else
            {
                //Player has to go to the point
                Player.Input.RunX = LagDistance.normalized.x;
                Player.Input.RunZ = LagDistance.normalized.z;
            }

            //jump if the remote player is higher than the player on the current client
            Player.Input.Jump = RemotePlayerPosition.y - transform.position.y > 0.2f;

            //Look Smooth
            Player.Input.LookX = Mathf.SmoothDamp(Player.Input.LookX, RemoteLookX, ref LookXVel, 0.2f);
            Player.Input.LookZ = Mathf.SmoothDamp(Player.Input.LookZ, RemoteLookZ, ref LookZVel, 0.2f);

        }

        public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
        {
            if (stream.IsWriting)
            {
                stream.SendNext(transform.position);
                stream.SendNext(Player.Input.LookX);
                stream.SendNext(Player.Input.LookZ);
                stream.SendNext(Player.State);
            }
            else
            {
                RemotePlayerPosition = (Vector3)stream.ReceiveNext();
                RemoteLookX = (float)stream.ReceiveNext();
                RemoteLookZ = (float)stream.ReceiveNext();
                var state = (Player.PlayerState)stream.ReceiveNext();

                //Enter
                if (state == Player.PlayerState.TRANSITION && Player.State == Player.PlayerState.NORMAL)
                    Player.StartCoroutine(Player.EnterCarAnimation());
                //Exit
                if (state == Player.PlayerState.TRANSITION && Player.State == Player.PlayerState.IN_CAR)
                    Player.StartCoroutine(Player.ExitCarAnimation());


                Player.State = state;
            }
        }
    }
}

If your character was moved with physics, RPC calling the positional and angular velocity to the rigidbody along with the actual position and rotation makes for relatively convincing in-between movement, between networked update calls.

In your case of course, it probably isn’t possible to cheat using physics since your guy doesnt have a rigidbody in the first place. Using lerp however follows the same logic. You want to make in-betweens, between networked update calls, not every frame.

I don’t recall the sample scenes in photon having such issues though, maybe check those out in more detail to see what they use and what they are doing?

1 Like

i dont use a rigidbody
the player move on a spline
the legs are an animation

I was actually referring Unity’s Multiplayer Games (sub-forum), I’m pretty sure there’s a photon thread. (At least it was there 2 years ago).

This means something else modifies the position / rotation. Well, unless network position is a bit out of sync.

MoveTowards on its own won’t produce laggy results.
Can you double check that you’re not altering position / rotation anywhere else?

Also, are you by any chance running server on the same machine?
It could be that server receives less process quota (when Unity server is not focused), potentially reducing FPS, which may lead to stuttering on the clients as well.

1 Like

I am running on the same machine, but i have in both windows stable 60FPS
i have only one dreamteck spline follow controller that moves the player.

I destroy the client component when it is not mine:

 if(!GetComponent<PhotonView>().IsMine)
        {
            Destroy(GetComponent<SplineFollower>());
            tag = "OtherPlayer";
        }

thats why i am confused, because local i should not have any problem with this stutter.

Try removing PhotonTransformView completely and syncing only via NetworkPlayer instead.

1 Like

same issue:
Here is my complete moving code:
I use a snap code to snap to ground

using UnityEngine;
using UnityEngine.Events;
using System.Collections;
using System.Collections.Generic;

namespace Dreamteck.Splines
{
    public delegate void SplineReachHandler();
    [AddComponentMenu("Dreamteck/Splines/Spline Follower")]
    public class SplineFollower : SplineTracer
    {
        public enum FollowMode { Uniform, Time }
        public enum Wrap { Default, Loop, PingPong }
        [HideInInspector]
        public Wrap wrapMode = Wrap.Default;
        [HideInInspector]
        public FollowMode followMode = FollowMode.Uniform;

        [HideInInspector]
        public bool autoStartPosition = false;

        [HideInInspector]
        public bool autoFollow = true;
        /// <summary>
        /// Used when follow mode is set to Uniform. Defines the speed of the follower
        /// </summary>
        public float followSpeed
        {
            get { return _followSpeed; }
            set
            {
                if (_followSpeed != value)
                {
                    if (value < 0f) value = 0f;
                    _followSpeed = value;
                }
            }
        }

        /// <summary>
        /// Used when follow mode is set to Time. Defines how much time it takes for the follower to travel through the path
        /// </summary>
        public float followDuration
        {
            get { return _followDuration; }
            set
            {
                if (_followDuration != value)
                {
                    if (value < 0f) value = 0f;
                    _followDuration = value;
                }
            }
        }

        [System.Obsolete("Deprecated in 1.0.8. Use result instead")]
        public SplineResult followResult
        {
            get { return _result; }
        }

        [System.Obsolete("Deprecated in 1.0.8. Use offsettedResult instead")]
        public SplineResult offsettedFollowResult
        {
            get { return offsettedResult;  }
        }

        public event SplineReachHandler onEndReached;
        public event SplineReachHandler onBeginningReached;

        [SerializeField]
        [HideInInspector]
        private float _followSpeed = 1f;

        [SerializeField]
        [HideInInspector]
        private float _followDuration = 1f;

        private double lastClippedPercent = -1.0;

        private SplineResult from = new SplineResult();
        private SplineResult to = new SplineResult();
        private bool followStarted = false;

        //Snap Object
        public GameObject road; //used to snap the bike on the road  //hide in inspector?
        //GameObject computer;
        //Animation
        public Animator riderAnimator; //to play the pedaling animation when needed
        public Animator wheelsAnimator; //to make the wheels play their rotate animation when needed
        public Transform target; //waypoint target
        public GameObject player;

       



#if UNITY_EDITOR
        public bool editorSetPosition = false;  //true
#endif

        private BicyclePowerSim bicycleSim;
       

        protected override void Start()
        {
            bicycleSim = GetComponent<BicyclePowerSim>(); //get the BicyclePower Sim component
            base.Start();
            if (autoFollow && autoStartPosition) SetPercent(Project(GetTransform().position).percent);
            //road = GameObject.FindWithTag("ground");
            road = GameObject.Find("Level/ground");

            computer = GameObject.FindWithTag("SplinePath").GetComponent<SplineComputer>();

            //What Bicycle is active?
            //CheckBicycle();


        }

        protected override void LateRun()
        {
            base.LateRun();
            if (autoFollow) AutoFollow();
        }

        protected override void PostBuild()
        {
            base.PostBuild();
            if (samples.Length == 0) return;
            EvaluateClipped(_result, ClipPercent(_result.percent));
            if (autoFollow && !autoStartPosition) ApplyMotion();
        }



        private bool isInAeroPosition = false; //toggle to change the frontal area, affecting aero drag
        private float power = 0; //the leg power we input on the bike
                                 //Snap bike to the ground

        private float posShifter = 0.0f;
       
       // private float maxShifter = 0.0f;
        void Update()
        {
            if (Input.GetKeyUp("left"))
            {
                shiftLeft();
                /*if (splineFollower.motion.offset.x <= -2.1f)
                {
                    splineFollower.motion.offset = new Vector3(0f, 0f);
                }*/
            }

            if (splineFollower.motion.offset.x <= -2.0f)
            {
                splineFollower.motion.offset = new Vector3(0f, 0f);
            }

            if (Input.GetKeyUp("right"))
            {
                shiftRight();
                if (splineFollower.motion.offset.x >= 2.1f)
                {
                    splineFollower.motion.offset = new Vector3(0f, 0f);
                }
            }

           
           


        }

        public void shiftLeft()
        {
            posShifter -= 0.2f;
          //  Mathf.Clamp(posShifter, -2f, 2f);
            splineFollower.motion.offset = new Vector3(posShifter, 0f);
            Debug.Log("left pressed");
        }

        public void shiftRight()
        {

            posShifter += 0.2f;
        //    Mathf.Clamp(posShifter, -2f, 2f);
            splineFollower.motion.offset = new Vector3(posShifter, 0f);
            Debug.Log("right pressed");
           
        }


        public void Snap()
        {
            if (gameObject.tag == "Player")
            {

                //Raycast to get the slope normal and the road Y position to snap the bike on the road

                RaycastHit hit = new RaycastHit();

                float snapYPosition = this.transform.position.y;
                Vector3 normal = Vector3.up;

                if (motion.applyPositionY == true)
                {
                    motion.applyPositionY = false;
                    Debug.Log("Set me false");
                }





                if (Physics.Raycast(new Vector3(transform.position.x, transform.position.y + 0.1f, transform.position.z), -Vector3.up, out hit, Mathf.Infinity))
                {
                    snapYPosition = hit.point.y;
                    normal = hit.normal;
                }


                Vector3 position = this.transform.position;
                position.y = snapYPosition;

                this.transform.position = position;

                //get the inclinaison angle
                Vector3 temp = Vector3.Cross(normal, transform.forward);
                Vector3 d = Vector3.Cross(temp, normal);
                float angle = Mathf.Sign(d.y) * Mathf.Acos(Mathf.Clamp(normal.y, -1f, 1f));

                //set the slope grade
                bicycleSim.slopeGrade = Mathf.Lerp(bicycleSim.slopeGrade, Mathf.Tan(angle) * 100, Time.deltaTime * 5);

                //control input

                power = 0; // no input we set the power at 0

                // if (Input.GetKey(KeyCode.Space))
                //               power = 200; //force 200 watts of power on the bike
                //
                //           if (Input.GetMouseButton(0))
                //               power = bicycleSim.GetPowerforSpeed(20); //find the power needed to reach 20 km/h
                //
                //
                if (Input.GetMouseButton(1))
                    bicycleSim.SetBrakeForce(1); //apply full brake force
                else
                    bicycleSim.SetBrakeForce(0);

                if (Input.GetKeyDown(KeyCode.T))
                {

                    if (isInAeroPosition)
                    {
                        isInAeroPosition = false;
                        bicycleSim.frontalArea = 0.5f;

                    }
                    else
                    {
                        isInAeroPosition = true;
                        bicycleSim.frontalArea = 0.4f;
                    }
                    riderAnimator.SetBool("aero", isInAeroPosition); //set mecanim to aero position
                }


                //set the input power on the bike
                bicycleSim.SetPower(power);

                //move the gameObject
                Vector3 direction = (target.transform.position - this.transform.position).normalized;  //target
                Vector3 newPos = transform.position + direction * (bicycleSim.speed / 3.6f) * Time.deltaTime;
                newPos.y = snapYPosition;
                // transform.position = newPos;

                //rotate the gameObject


                Vector3 viewingVector = target.transform.position - transform.position;
                viewingVector.y = 0;
                if (viewingVector != Vector3.zero)
                {
                    Quaternion targetRotation = Quaternion.LookRotation(viewingVector);
                    transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, 2f * Time.deltaTime);
                }

                Quaternion slopeRotation = Quaternion.LookRotation(Vector3.Cross(transform.right, normal), normal);
                transform.rotation = Quaternion.Slerp(transform.rotation, slopeRotation, 5f * Time.deltaTime);

                //Animation of the Rider
                //riderAnimator.speed = Mathf.Min(power / 50f, 2);
                riderAnimator.speed = 0;
                if (bicycleSim.centralSensor.GetCadence() > 0)
                {
                    riderAnimator.speed = Mathf.Min(bicycleSim.centralSensor.GetCadence() / 50f, 2);
                }
                // riderAnimator.speed = Mathf.Min(GameObject.Find("FitnessEquipmentDisplay").GetComponent<FitnessEquipmentDisplay>().cadence / 50f, 2);
                wheelsAnimator.speed = Mathf.Min(bicycleSim.speed / 3.6f, 3);
            }
        }

        //end try

        


        void AutoFollow()
        {
            if (gameObject.tag == "Player")
            {
                _followSpeed = bicycleSim.speed;
            }
            else
            {
                _followSpeed = followSpeed;
            }
            if (!followStarted && autoStartPosition) SetPercent(Project(GetTransform().position).percent);
            followStarted = true;
            switch (followMode)
                 {

               //   case FollowMode.Uniform: Move(Time.deltaTime * _followSpeed); break;  //our bicycleSimSpeed here!?
              //  case FollowMode.Uniform: Move ((Time.deltaTime + _followSpeed) * (bicycleSim.speed / 3.6f)); break;
             //   case FollowMode.Uniform: Move (_followSpeed * (bicycleSim.speed / 3.6f)); break;
                case FollowMode.Uniform: Move(_followSpeed * Time.deltaTime / 3.6f); break;



                case FollowMode.Time:
                    if(_followDuration == 0.0) Move(0.0);
                    else Move((double)Time.deltaTime / _followDuration);
                    break;
            }
        }

        public void Restart(double startPosition = 0.0)
        {
            ResetTriggers();
            followStarted = false;
            SetPercent(startPosition);
        }

        public override void SetPercent(double percent, bool checkTriggers = false)
        {
            if (clippedSamples.Length == 0) GetClippedSamplesImmediate();
            base.SetPercent(percent, checkTriggers);
            lastClippedPercent = percent;
        }

        public override void SetDistance(float distance, bool checkTriggers = false)
        {
            base.SetDistance(distance, checkTriggers);
            lastClippedPercent = ClipPercent(_result.percent);
            if (samplesAreLooped && clipFrom == clipTo && distance > 0f && lastClippedPercent == 0.0) lastClippedPercent = 1.0;
        }

        public void Move(double percent)
        {
            if(percent == 0.0) return;
            if (clippedSamples.Length <= 1)
            {
                if (clippedSamples.Length == 1)
                {
                    _result.CopyFrom(clippedSamples[0]);
                    ApplyMotion();
                }
                return;
            }
           
            EvaluateClipped(_result, ClipPercent(_result.percent));
            double startPercent = ClipPercent(_result.percent);
            if (wrapMode == Wrap.Default && lastClippedPercent >= 1.0 && startPercent == 0.0) startPercent = 1.0;
            double p = startPercent + (_direction == Spline.Direction.Forward ? percent : -percent);
            bool callOnEndReached = false, callOnBeginningReached = false;
            lastClippedPercent = p;
            if(_direction == Spline.Direction.Forward && p >= 1.0)
            {
                if (onEndReached != null && startPercent < 1.0) callOnEndReached = true;
                switch (wrapMode)
                {
                    case Wrap.Default:
                        p = 1.0;
                        break;
                    case Wrap.Loop:
                        CheckTriggersClipped(UnclipPercent(startPercent), UnclipPercent(1.0));
                        while (p > 1.0) p -= 1.0;
                        startPercent = 0.0;
                        break;
                    case Wrap.PingPong:
                        p = DMath.Clamp01(1.0-(p-1.0));
                        startPercent = 1.0;
                        _direction = Spline.Direction.Backward;
                        break;
                }
            } else if(_direction == Spline.Direction.Backward && p <= 0.0)
            {
                if (onBeginningReached != null && startPercent > 0.0) callOnBeginningReached = true;
                switch (wrapMode)
                {
                    case Wrap.Default:
                        p = 0.0;
                        break;
                    case Wrap.Loop:
                        CheckTriggersClipped(UnclipPercent(startPercent), UnclipPercent(0.0));
                        while (p < 0.0) p += 1.0;
                        startPercent = 1.0;
                        break;
                    case Wrap.PingPong:
                        p = DMath.Clamp01(-p);
                        startPercent = 0.0;
                        _direction = Spline.Direction.Forward;
                        break;
                }
            }
            CheckTriggersClipped(UnclipPercent(startPercent), UnclipPercent(p));
            Evaluate(_result, UnclipPercent(p));
            ApplyMotion();
            if (callOnEndReached) onEndReached();
            else if (callOnBeginningReached) onBeginningReached();
            InvokeTriggers();
        }

        public void Move(float distance)
        {
            if (distance < 0f) distance = 0f;
            if (distance == 0f) return;
            if (samples.Length <= 1)
            {
                if (samples.Length == 1)
                {
                    _result.CopyFrom(samples[0]);
                    ApplyMotion();
                }
                return;
            }
            bool callOnEndReached = false, callOnBeginningReached = false;

            float moved = 0f;
            int nextIndex = 0;
            double clippedPercent = ClipPercent(_result.percent);
            if (clippedPercent == 0.0 && lastClippedPercent == 1.0 && clipFrom == clipTo) clippedPercent = 1.0;
            from.CopyFrom(_result);
            to.CopyFrom(_result);

            if (_direction == Spline.Direction.Forward)
            {

                nextIndex = DMath.FloorInt(clippedPercent * (clippedSamples.Length - 1))-1;
                if (nextIndex < 0) nextIndex = 0;
                for (int i = nextIndex; i < clippedSamples.Length-1; i++)
                {
                    if (ClipPercent(clippedSamples[i].percent) > clippedPercent) break;
                    nextIndex = i;
                }
            }
            else
            {
                nextIndex = DMath.CeilInt(clippedPercent * (clippedSamples.Length - 1)) + 1;
                if (nextIndex >= clippedSamples.Length) nextIndex = clippedSamples.Length - 1;
                for (int i = nextIndex; i >= 1; i--)
                {
                    double percent = ClipPercent(clippedSamples[i].percent);
                    if (i == clippedSamples.Length - 1) percent = 1.0;
                    if (percent < clippedPercent) break;
                    nextIndex = i;
                }
            }
            while (moved < distance)
            {
                from.CopyFrom(to); //Get the current sample
                if (_direction == Spline.Direction.Forward)
                {
                    nextIndex++;
                    if ((samplesAreLooped && _result.percent >= clippedSamples[clippedSamples.Length - 1].percent && _result.percent < clippedSamples[0].percent) || (!samplesAreLooped && _result.percent >= clippedSamples[clippedSamples.Length - 1].percent) || nextIndex >= clippedSamples.Length)
                    {
                        if (onEndReached != null && lastClippedPercent < 1.0) callOnEndReached = true;
                        if (wrapMode == Wrap.Default)
                        {
                            lastClippedPercent = 1.0;
                            _result.CopyFrom(clippedSamples[clippedSamples.Length - 1]);
                            CheckTriggersClipped(from.percent, _result.percent);
                            break;
                        }
                        else if (wrapMode == Wrap.Loop)
                        {
                            CheckTriggersClipped(from.percent, clippedSamples[clippedSamples.Length-1].percent);
                            from.CopyFrom(clippedSamples[0]);
                            nextIndex = 1;
                        }
                        else if (wrapMode == Wrap.PingPong)
                        {
                            _direction = Spline.Direction.Backward;
                            CheckTriggersClipped(from.percent, clippedSamples[clippedSamples.Length - 1].percent);
                            from.CopyFrom(clippedSamples[clippedSamples.Length - 1]);
                            nextIndex = clippedSamples.Length - 2;
                        }
                    }
                }
                else
                {
                    nextIndex--;
                    if ((samplesAreLooped && _result.percent <= clippedSamples[0].percent && _result.percent > clippedSamples[clippedSamples.Length - 1].percent) || (!samplesAreLooped && _result.percent <= clippedSamples[0].percent) || nextIndex < 0)
                    {
                        if (onBeginningReached != null && lastClippedPercent > 0.0) callOnBeginningReached = true;
                        if (wrapMode == Wrap.Default)
                        {
                            lastClippedPercent = 0.0;
                            _result.CopyFrom(clippedSamples[0]);
                            CheckTriggersClipped(from.percent, _result.percent);
                            break;
                        }
                        else if (wrapMode == Wrap.Loop)
                        {
                            CheckTriggersClipped(from.percent, clippedSamples[0].percent);
                            from.CopyFrom(clippedSamples[clippedSamples.Length - 1]);
                            nextIndex = clippedSamples.Length - 2;
                        }
                        else if (wrapMode == Wrap.PingPong)
                        {
                            _direction = Spline.Direction.Forward;
                            CheckTriggersClipped(from.percent, clippedSamples[0].percent);
                            from.CopyFrom(clippedSamples[0]);
                            nextIndex = 1;
                        }
                    }
                }
                lastClippedPercent = ClipPercent(_result.percent);
                to.CopyFrom(clippedSamples[nextIndex]); //Get the next sample
                float traveled = Vector3.Distance(to.position, from.position);
                moved += traveled;
                if (moved >= distance)
                {
                    float excess = moved - distance;
                    double lerpPercent = 1.0 - excess / traveled;
                    SplineResult.Lerp(from, to, lerpPercent, _result);
                    if (samplesAreLooped)
                    {
                        if (_direction == Spline.Direction.Forward && from.percent > to.percent) _result.percent = DMath.Lerp(from.percent, 1.0, lerpPercent); 
                        else if (_direction == Spline.Direction.Backward && from.percent < to.percent) _result.percent = DMath.Lerp(1.0, to.percent, lerpPercent);
                    }
                    CheckTriggersClipped(from.percent, _result.percent);
                    break;
                }
                CheckTriggersClipped(from.percent, to.percent);
            }

            ApplyMotion();
            if (callOnEndReached) onEndReached();
            else if (callOnBeginningReached) onBeginningReached();
            InvokeTriggers();
        }
    }
}

hmm i have Camera in scene with the target Player view: MayBe it is better to put the camera to the player direct?

its the same i tried :frowning:

makes no sense i deactivate the dreamteck spline when is not mine.
i also deactivate other parts. The remote player have this:

I think i got it, will see how it work @ the end