PhotonRigidbodyView using local space

Options
Has anyone adapted PhotonRigidbodyView to transmit and receive velocities in local space coords?

This may seem like a strange request at first but I'm creating a mixed-reality experience in which I physically align people's virtual environments (and everything in them). The only way for me to do this is to translate a 'rootspace' parent on each player which contains the environment (due to my setup, moving the player's POV won't help). For transforms, I saw that PhotonTransformViewClassic operates in local space and this has been very effective. But for rigidbodies, moving the parent transform on players who don't own the rigidbody causes very glitchy effects (wobbling crazy motion).

If no-one's done this before, my plan was to go through PhotonRigidbodyView and adjust all transmitted velocities to be relative to a parent (if present), then velocity back to world space based on any parent present when received before applying. Does that sound like the right approach?

Comments

  • S_Oliver
    Options
    You could easily copy n pase the code from PhotonRigidbodyView.cs and rewrite it to fit ur needs. It is pretty simple code. So yes go through it.
  • jonb
    Options
    Thanks, I'll do that. If anyone needs this in the future:
    namespace Photon.Pun
    {
        using UnityEngine;
    #if UNITY_EDITOR
        using UnityEditor;
    
    #endif
    
        /// <summary>
        /// transmits and applies rigidbody velocities in local space
        /// </summary>
        [RequireComponent(typeof(PhotonView))]
        [RequireComponent(typeof(Rigidbody))]
        [AddComponentMenu("Photon Networking/Photon Rigidbody View")]
        public class PhotonLocalRigidbodyView : MonoBehaviour, IPunObservable
        {
            private float m_Distance;
            private float m_Angle;
    
            private Rigidbody m_Body;
    
            private PhotonView m_PhotonView;
    
            private Vector3 m_NetworkPosition;
    
            private Quaternion m_NetworkRotation;
    
            public bool m_SynchronizeVelocity = true;
            public bool m_SynchronizeAngularVelocity = false;
    
            public bool m_TeleportEnabled = false;
            public float m_TeleportIfDistanceGreaterThan = 3.0f;
    
            public void Awake()
            {
                this.m_Body = GetComponent<Rigidbody>();
                this.m_PhotonView = GetComponent<PhotonView>();
    
                this.m_NetworkPosition = new Vector3();
                this.m_NetworkRotation = new Quaternion();
            }
    
            public void FixedUpdate()
            {
                if (!this.m_PhotonView.IsMine)
                {
                    // convert m_NetworkPosition to world
                    this.m_Body.position = Vector3.MoveTowards(this.m_Body.position, ToWorldPos(this.m_NetworkPosition), this.m_Distance * (1.0f / PhotonNetwork.SerializationRate));
                    // this.m_Body.position = Vector3.MoveTowards(this.m_Body.position, this.m_NetworkPosition, this.m_Distance * (1.0f / PhotonNetwork.SerializationRate));
                    // convert m_NetworkRotation to world
                    this.m_Body.rotation = Quaternion.RotateTowards(this.m_Body.rotation, ToWorldRot(this.m_NetworkRotation), this.m_Angle * (1.0f / PhotonNetwork.SerializationRate));
                    // this.m_Body.rotation = Quaternion.RotateTowards(this.m_Body.rotation, this.m_NetworkRotation, this.m_Angle * (1.0f / PhotonNetwork.SerializationRate));
                }
            }
    
            public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
            {
                if (stream.IsWriting)
                {
                    // change to local position
                    stream.SendNext(this.m_Body.GetLocalPosition());
                    //stream.SendNext(this.m_Body.position);
                    // change to local rotation
                    stream.SendNext(this.m_Body.GetLocalRotation());
                    // stream.SendNext(this.m_Body.rotation);
    
                    if (this.m_SynchronizeVelocity)
                    {
                        // convert to local
                        stream.SendNext(this.m_Body.GetLocalVelocity());
                        // stream.SendNext(this.m_Body.velocity);
                    }
    
                    if (this.m_SynchronizeAngularVelocity)
                    {
                        // convert to local
                        stream.SendNext(this.m_Body.GetLocalAngularVelocity());
                        // stream.SendNext(this.m_Body.angularVelocity);
                    }
                }
                else
                {
                    this.m_NetworkPosition = (Vector3) stream.ReceiveNext();
                    this.m_NetworkRotation = (Quaternion) stream.ReceiveNext();
    
                    if (this.m_TeleportEnabled)
                    {
                        // convert position to local for distance check (or m_NetworkPosition to world)
                        if (Vector3.Distance(this.m_Body.GetLocalPosition(), this.m_NetworkPosition) > this.m_TeleportIfDistanceGreaterThan)
                            // if (Vector3.Distance(this.m_Body.position, this.m_NetworkPosition) > this.m_TeleportIfDistanceGreaterThan)
                        {
                            // convert m_NetworkPosition to world
                            this.m_Body.position = ToWorldPos(this.m_NetworkPosition);
                            // this.m_Body.position = this.m_NetworkPosition;
                        }
                    }
    
                    if (this.m_SynchronizeVelocity || this.m_SynchronizeAngularVelocity)
                    {
                        float lag = Mathf.Abs((float) (PhotonNetwork.Time - info.SentServerTime));
    
                        if (this.m_SynchronizeVelocity)
                        {
                            // convert stream to world
                            this.m_Body.velocity = ToWorldVelocity((Vector3) stream.ReceiveNext());
                            // this.m_Body.velocity = (Vector3)stream.ReceiveNext();
    
                            // convert velocity to local
                            this.m_NetworkPosition += this.m_Body.GetLocalVelocity() * lag;
                            // this.m_NetworkPosition += this.m_Body.velocity * lag;
    
                            // convert position to local
                            this.m_Distance = Vector3.Distance(this.m_Body.GetLocalPosition(), this.m_NetworkPosition);
                            // this.m_Distance = Vector3.Distance(this.m_Body.position, this.m_NetworkPosition);
                        }
    
                        if (this.m_SynchronizeAngularVelocity)
                        {
                            // convert stream to world
                            this.m_Body.angularVelocity = ToWorldAngularVelocity((Vector3) stream.ReceiveNext());
                            // this.m_Body.angularVelocity = (Vector3)stream.ReceiveNext();
    
                            // convert angularVelocity to local
                            this.m_NetworkRotation = Quaternion.Euler(this.m_Body.GetLocalAngularVelocity() * lag) * this.m_NetworkRotation;
                            // this.m_NetworkRotation = Quaternion.Euler(this.m_Body.angularVelocity * lag) * this.m_NetworkRotation;
    
                            // convert rotation to local
                            this.m_Angle = Quaternion.Angle(this.m_Body.GetLocalRotation(), this.m_NetworkRotation);
                            // this.m_Angle = Quaternion.Angle(this.m_Body.rotation, this.m_NetworkRotation);
                        }
                    }
                }
            }
    
            private Vector3 ToWorldPos(Vector3 localPosition)
            {
                return transform.parent.TransformPoint(localPosition);
            }
    
            private Quaternion ToWorldRot(Quaternion localRotation)
            {
                return transform.parent.rotation * localRotation;
            }
    
            private Vector3 ToWorldVelocity(Vector3 localVelocity)
            {
                return transform.parent.TransformVector(localVelocity);
            }
    
            private Vector3 ToWorldAngularVelocity(Vector3 localAngularScale)
            {
                return transform.parent.InverseTransformDirection(localAngularScale);
            }
        }
    
        /// <summary>
        /// extension methods to rigidbody to get local versions of stuff (pass in transform parent as param)
        /// </summary>
        public static class RigidBodyExtensions
        {
            public static Vector3 GetLocalPosition(this Rigidbody rb)
            {
                return rb.transform.localPosition;
            }
    
            public static Quaternion GetLocalRotation(this Rigidbody rb)
            {
                return rb.transform.localRotation;
            }
    
            public static Vector3 GetLocalVelocity(this Rigidbody rb)
            {
                // trying InverseTransformVector as I think we don't want to factor in position, only direction and scale?
                return rb.transform.parent.InverseTransformVector(rb.velocity);
            }
    
            public static Vector3 GetLocalAngularVelocity(this Rigidbody rb)
            {
                // trying InverseTransformDirection as we only want direction not affected by scale or position?
                return rb.transform.parent.InverseTransformDirection(rb.velocity);
            }
        }
    
        /// <summary>
        /// Custom editor duplicated from original
        /// </summary>
    #if UNITY_EDITOR
        [CustomEditor(typeof(PhotonLocalRigidbodyView))]
        public class PhotonLocalRigidbodyViewEditor : Editor
        {
            public override void OnInspectorGUI()
            {
                if (Application.isPlaying)
                {
                    EditorGUILayout.HelpBox("Editing is disabled in play mode.", MessageType.Info);
                    return;
                }
    
                PhotonLocalRigidbodyView view = (PhotonLocalRigidbodyView) target;
    
                view.m_TeleportEnabled = PhotonGUI.ContainerHeaderToggle("Enable teleport for large distances", view.m_TeleportEnabled);
    
                if (view.m_TeleportEnabled)
                {
                    Rect rect = PhotonGUI.ContainerBody(20.0f);
                    view.m_TeleportIfDistanceGreaterThan = EditorGUI.FloatField(rect, "Teleport if distance greater than", view.m_TeleportIfDistanceGreaterThan);
                }
    
                view.m_SynchronizeVelocity = PhotonGUI.ContainerHeaderToggle("Synchronize Velocity", view.m_SynchronizeVelocity);
                view.m_SynchronizeAngularVelocity = PhotonGUI.ContainerHeaderToggle("Synchronize Angular Velocity", view.m_SynchronizeAngularVelocity);
    
                if (GUI.changed)
                {
                    EditorUtility.SetDirty(view);
                }
            }
        }
    #endif
    }