/// <summary>
        /// Create a State from a SmoothSync script.
        /// </summary>
        /// <remarks>
        /// This is called on owners when creating the States to be passed over the network.
        /// </remarks>
        /// <param name="smoothSyncScript"></param>
        public State(SmoothSync smoothSyncScript)
        {
#if UNITY_WEBGL
            ownerTimestamp = (int)(Time.time * 1000.0f);
#else
            ownerTimestamp = NetworkTransport.GetNetworkTimestamp();
#endif
            position = smoothSyncScript.getPosition();
            rotation = smoothSyncScript.getRotation();
            scale    = smoothSyncScript.getScale();

            if (smoothSyncScript.hasRigdibody)
            {
                velocity        = smoothSyncScript.rb.velocity;
                angularVelocity = smoothSyncScript.rb.angularVelocity;
            }
            else if (smoothSyncScript.hasRigidbody2D)
            {
                velocity        = smoothSyncScript.rb2D.velocity;
                angularVelocity = new Vector3(0, 0, smoothSyncScript.rb2D.angularVelocity);
            }
            else
            {
                velocity        = Vector3.zero;
                angularVelocity = Vector3.zero;
            }
        }
Example #2
0
        public void copyFromSmoothSync(SmoothSync smoothSyncScript)
        {
            ownerTimestamp = Time.realtimeSinceStartup;
            position       = smoothSyncScript.getPosition();
            rotation       = smoothSyncScript.getRotation();
            scale          = smoothSyncScript.getScale();

            if (smoothSyncScript.hasRigidbody)
            {
                velocity        = smoothSyncScript.rb.velocity;
                angularVelocity = smoothSyncScript.rb.angularVelocity * Mathf.Rad2Deg;
            }
            else if (smoothSyncScript.hasRigidbody2D)
            {
                velocity          = smoothSyncScript.rb2D.velocity;
                angularVelocity.x = 0;
                angularVelocity.y = 0;
                angularVelocity.z = smoothSyncScript.rb2D.angularVelocity;
            }
            else
            {
                velocity        = Vector3.zero;
                angularVelocity = Vector3.zero;
            }
            //atPositionalRest = smoothSyncScript.sendAtPositionalRestMessage;
            //atRotationalRest = smoothSyncScript.sendAtRotationalRestMessage;
        }
Example #3
0
 /// <summary>
 /// Cache a reference to the rigidbody component and Smooth Sync scripts.
 /// </summary>
 void Start()
 {
     rb         = GetComponent <Rigidbody>();
     rb2D       = GetComponent <Rigidbody2D>();
     smoothSync = GetComponent <SmoothSync>();
     // Set up a validation method to check incoming States to see if cheating may be happening.
     smoothSync.validateStateMethod = validateStateOfPlayer;
 }
Example #4
0
    private void Awake()
    {
        _smoothSync     = gameObject.GetComponent <SmoothSync>();
        _gunControl     = gameObject.GetComponent <PlayerGunControl>();
        rigidbody       = gameObject.GetComponent <Rigidbody>();
        _networkManager = gameObject.GetComponent <PlayerNetworkManager>();
        renderer        = gameObject.GetComponentInChildren <Renderer>();
        baseMaterial    = renderer.material;
        cam             = Camera.main;

        footstep = footstepTimer;
    }
Example #5
0
        public override void OnInspectorGUI()
        {
            SmoothSync myTarget = (SmoothSync)target;

            if (myTarget.childObjectToSync)
            {
                Color oldColor = GUI.contentColor;
                GUI.contentColor = Color.green;
                EditorGUILayout.LabelField("Syncing child", myTarget.childObjectToSync.name);
                GUI.contentColor = oldColor;
            }
            DrawDefaultInspector();
        }
        /// <summary>
        /// Deserialize a message from the network.
        /// </summary>
        /// <remarks>
        /// Only receives what it needs and decompresses floats if you chose to.
        /// </remarks>
        /// <param name="writer">The Networkreader to read from.</param>
        override public void Deserialize(NetworkReader reader)
        {
            // The first received byte tells us what we need to be syncing.
            byte syncInfoByte        = reader.ReadByte();
            bool syncPosition        = shouldSyncPosition(syncInfoByte);
            bool syncRotation        = shouldSyncRotation(syncInfoByte);
            bool syncScale           = shouldSyncScale(syncInfoByte);
            bool syncVelocity        = shouldSyncVelocity(syncInfoByte);
            bool syncAngularVelocity = shouldSyncAngularVelocity(syncInfoByte);

            NetworkInstanceId netID = reader.ReadNetworkId();
            int syncIndex           = (int)reader.ReadPackedUInt32();

            state.ownerTimestamp = (int)reader.ReadPackedUInt32();

            // Find the GameObject
            GameObject ob = null;

            if (NetworkServer.active)
            {
                ob = NetworkServer.FindLocalObject(netID);
            }
            else
            {
                ob = ClientScene.FindLocalObject(netID);
            }

            if (!ob)
            {
                Debug.LogWarning("Could not find target for network state message.");
                return;
            }

            // It doesn't matter which SmoothSync is returned since they all have the same list.
            smoothSync = ob.GetComponent <SmoothSync>();

            // If we want the server to relay non-owned object information out to other clients, set these variables so we know what we need to send.
            if (NetworkServer.active && !smoothSync.hasAuthority)
            {
                state.serverShouldRelayPosition        = syncPosition;
                state.serverShouldRelayRotation        = syncRotation;
                state.serverShouldRelayScale           = syncScale;
                state.serverShouldRelayVelocity        = syncVelocity;
                state.serverShouldRelayAngularVelocity = syncAngularVelocity;
            }

            // Find the correct object to sync according to the syncIndex.
            for (int i = 0; i < smoothSync.childObjectSmoothSyncs.Length; i++)
            {
                if (smoothSync.childObjectSmoothSyncs[i].syncIndex == syncIndex)
                {
                    smoothSync = smoothSync.childObjectSmoothSyncs[i];
                }
            }

            if (!smoothSync)
            {
                Debug.LogWarning("Could not find target for network state message.");
                return;
            }

            // Read position.
            if (syncPosition)
            {
                if (smoothSync.isPositionCompressed)
                {
                    if (smoothSync.isSyncingXPosition)
                    {
                        state.position.x = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingYPosition)
                    {
                        state.position.y = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingZPosition)
                    {
                        state.position.z = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                }
                else
                {
                    if (smoothSync.isSyncingXPosition)
                    {
                        state.position.x = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingYPosition)
                    {
                        state.position.y = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingZPosition)
                    {
                        state.position.z = reader.ReadSingle();
                    }
                }
            }
            else
            {
                if (smoothSync.stateCount > 0)
                {
                    state.position = smoothSync.stateBuffer[0].position;
                }
                else
                {
                    state.position = smoothSync.getPosition();
                }
            }
            // Read rotation.
            if (syncRotation)
            {
                Vector3 rot = new Vector3();
                if (smoothSync.isRotationCompressed)
                {
                    if (smoothSync.isSyncingXRotation)
                    {
                        rot.x = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingYRotation)
                    {
                        rot.y = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingZRotation)
                    {
                        rot.z = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    state.rotation = Quaternion.Euler(rot);
                }
                else
                {
                    if (smoothSync.isSyncingXRotation)
                    {
                        rot.x = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingYRotation)
                    {
                        rot.y = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingZRotation)
                    {
                        rot.z = reader.ReadSingle();
                    }
                    state.rotation = Quaternion.Euler(rot);
                }
            }
            else
            {
                if (smoothSync.stateCount > 0)
                {
                    state.rotation = smoothSync.stateBuffer[0].rotation;
                }
                else
                {
                    state.rotation = smoothSync.getRotation();
                }
            }
            // Read scale.
            if (syncScale)
            {
                if (smoothSync.isScaleCompressed)
                {
                    if (smoothSync.isSyncingXScale)
                    {
                        state.scale.x = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingYScale)
                    {
                        state.scale.y = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingZScale)
                    {
                        state.scale.z = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                }
                else
                {
                    if (smoothSync.isSyncingXScale)
                    {
                        state.scale.x = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingYScale)
                    {
                        state.scale.y = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingZScale)
                    {
                        state.scale.z = reader.ReadSingle();
                    }
                }
            }
            else
            {
                if (smoothSync.stateCount > 0)
                {
                    state.scale = smoothSync.stateBuffer[0].scale;
                }
                else
                {
                    state.scale = smoothSync.getScale();
                }
            }
            // Read velocity.
            if (syncVelocity)
            {
                if (smoothSync.isVelocityCompressed)
                {
                    if (smoothSync.isSyncingXVelocity)
                    {
                        state.velocity.x = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingYVelocity)
                    {
                        state.velocity.y = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingZVelocity)
                    {
                        state.velocity.z = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                }
                else
                {
                    if (smoothSync.isSyncingXVelocity)
                    {
                        state.velocity.x = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingYVelocity)
                    {
                        state.velocity.y = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingZVelocity)
                    {
                        state.velocity.z = reader.ReadSingle();
                    }
                }
            }
            else
            {
                state.velocity = Vector3.zero;
            }
            // Read anguluar velocity.
            if (syncAngularVelocity)
            {
                if (smoothSync.isAngularVelocityCompressed)
                {
                    if (smoothSync.isSyncingXAngularVelocity)
                    {
                        state.angularVelocity.x = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingYAngularVelocity)
                    {
                        state.angularVelocity.y = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingZAngularVelocity)
                    {
                        state.angularVelocity.z = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                }
                else
                {
                    if (smoothSync.isSyncingXAngularVelocity)
                    {
                        state.angularVelocity.x = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingYAngularVelocity)
                    {
                        state.angularVelocity.y = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingZAngularVelocity)
                    {
                        state.angularVelocity.z = reader.ReadSingle();
                    }
                }
            }
            else
            {
                state.angularVelocity = Vector3.zero;
            }
        }
 /// <summary>
 /// Create a NetworkState from a SmoothSync object.
 /// </summary>
 /// <param name="smoothSyncScript">The SmoothSync object</param>
 public NetworkState(SmoothSync smoothSyncScript)
 {
     this.smoothSync = smoothSyncScript;
     state           = new State(smoothSyncScript);
 }
Example #8
0
        public override void OnInspectorGUI()
        {
            SmoothSync myTarget = (SmoothSync)target;

            if (myTarget.childObjectToSync)
            {
                Color oldColor = GUI.contentColor;
                GUI.contentColor = Color.green;
                EditorGUILayout.LabelField("Syncing child", myTarget.childObjectToSync.name);
                GUI.contentColor = oldColor;
            }

            GUIContent contentWhenToUpdateTransform = new GUIContent("When to Update Transform", "Update will have smoother results but FixedUpdate may be better for physics.");

            EditorGUILayout.PropertyField(serializedObject.FindProperty("whenToUpdateTransform"), contentWhenToUpdateTransform);
            GUIContent contentInterpolationBackTime = new GUIContent("Interpolation Back Time", "How much time in the past non-owned objects should be. This is so if you hit a latency spike, you still have a buffer of the interpolationBackTime of known States before you start extrapolating into the unknown. Increasing will make interpolation more likely to be used, decreasing will make extrapolation more likely to be used. Keep above 1/SendRate to attempt to always interpolate. In seconds.");

            EditorGUILayout.PropertyField(serializedObject.FindProperty("interpolationBackTime"), contentInterpolationBackTime);

            GUIContent contentSendRate = new GUIContent("Send Rate", "How many times per second to send network updates.");

            EditorGUILayout.PropertyField(serializedObject.FindProperty("sendRate"), contentSendRate);
            GUIContent contentTimeCorrectionSpeed = new GUIContent("Time Correction Speed", "The estimated owner time will shift by at most this amount per second. Lower values will be smoother but may take time to adjust to larger jumps in latency. Keep this lower than ~.5 unless you are having serious latency issues.");

            EditorGUILayout.PropertyField(serializedObject.FindProperty("timeCorrectionSpeed"), contentTimeCorrectionSpeed);
            GUIContent contentPositionLerpSpeed = new GUIContent("Position Easing Speed", "How fast to ease to the new position on non-owned objects. 0 is never, 1 is instant.");

            EditorGUILayout.PropertyField(serializedObject.FindProperty("positionLerpSpeed"), contentPositionLerpSpeed);
            GUIContent contentRotationLerpSpeed = new GUIContent("Rotation Easing Speed", "How fast to ease to the new rotation on non-owned objects. 0 is never, 1 is instant.");

            EditorGUILayout.PropertyField(serializedObject.FindProperty("rotationLerpSpeed"), contentRotationLerpSpeed);
            GUIContent contentScaleLerpSpeed = new GUIContent("Scale Easing Speed", "How fast to ease to the new scale on non-owned objects. 0 is never, 1 is instant.");

            EditorGUILayout.PropertyField(serializedObject.FindProperty("scaleLerpSpeed"), contentScaleLerpSpeed);
            GUIContent contentNetworkChannel = new GUIContent("Network Channel", "The channel to send network updates on.");

            EditorGUILayout.PropertyField(serializedObject.FindProperty("networkChannel"), contentNetworkChannel);

            GUIContent contentChildObjectToSync = new GUIContent("Child Object to Sync", "Set this to sync a child object, leave blank to sync this object. Must leave one blank to sync the parent in order to sync children.");

            EditorGUILayout.PropertyField(serializedObject.FindProperty("childObjectToSync"), contentChildObjectToSync);

            GUIContent contentVariablesToSync = new GUIContent("Variables to Sync", "Fine tune what variables to sync.");

            showWhatToSync = EditorGUILayout.Foldout(showWhatToSync, contentVariablesToSync);
            if (showWhatToSync)
            {
                EditorGUI.indentLevel++;
                GUIContent contentSyncPosition = new GUIContent("Sync Position", "Fine tune what variables to sync.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("syncPosition"), contentSyncPosition);
                GUIContent contentSyncRotation = new GUIContent("Sync Rotation", "Fine tune what variables to sync");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("syncRotation"), contentSyncRotation);
                GUIContent contentSyncScale = new GUIContent("Sync Scale", "Fine tune what variables to sync");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("syncScale"), contentSyncScale);
                GUIContent contentSyncVelocity = new GUIContent("Sync Velocity", "Fine tune what variables to sync");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("syncVelocity"), contentSyncVelocity);
                GUIContent contentSyncAngularVelocity = new GUIContent("Sync Angular Velocity", "Fine tune what variables to sync");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("syncAngularVelocity"), contentSyncAngularVelocity);
                EditorGUI.indentLevel--;
            }

            GUIContent contentExtrapolation = new GUIContent("Extrapolation", "Extrapolation is going into the unknown based on information we had in the past. Generally, you'll want extrapolation to help fill in missing information during lag spikes.");

            showExtrapolation = EditorGUILayout.Foldout(showExtrapolation, contentExtrapolation);
            if (showExtrapolation)
            {
                EditorGUI.indentLevel++;
                GUIContent contentExtrapolationMode = new GUIContent("Extrapolation Mode", "None: No extrapolation. Limited: Some extrapolation. Unlimited: Unlimited extrapolation.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("extrapolationMode"), contentExtrapolationMode);
                if (myTarget.extrapolationMode == SmoothSync.ExtrapolationMode.Limited)
                {
                    GUIContent contentUseExtrapolationTimeLimit     = new GUIContent("Use Extrapolation Time Limit", "Whether or not you want to use extrapolationTimeLimit. You can use only the extrapolationTimeLimit and save a distance check every extrapolation frame.");
                    GUIContent contentUseExtrapolationDistanceLimit = new GUIContent("Use Extrapolation Distance Limit", "Whether or not you want to use extrapolationDistanceLimit. You can use only the extrapolationTimeLimit and save a distance check every extrapolation frame.");
                    GUIContent contentExtrapolationDistanceLimit    = new GUIContent("Extrapolation Distance Limit", "How much distance into the future a non-owned object is allowed to extrapolate. In distance units.");
                    GUIContent contentExtrapolationTimeLimit        = new GUIContent("Extrapolation Time Limit", "How much time into the future a non-owned object is allowed to extrapolate. In seconds.");
                    EditorGUILayout.PropertyField(serializedObject.FindProperty("useExtrapolationTimeLimit"), contentUseExtrapolationTimeLimit);
                    EditorGUILayout.PropertyField(serializedObject.FindProperty("extrapolationTimeLimit"), contentExtrapolationTimeLimit);
                    EditorGUILayout.PropertyField(serializedObject.FindProperty("useExtrapolationDistanceLimit"), contentUseExtrapolationDistanceLimit);
                    EditorGUILayout.PropertyField(serializedObject.FindProperty("extrapolationDistanceLimit"), contentExtrapolationDistanceLimit);
                }
                EditorGUI.indentLevel--;
            }

            GUIContent contentThresholds = new GUIContent("Thresholds", "Use thresholds to control when to send and set the transform.");

            showThresholds = EditorGUILayout.Foldout(showThresholds, contentThresholds);
            if (showThresholds)
            {
                EditorGUI.indentLevel++;
                GUIContent contentSnapTimeThreshold = new GUIContent("Snap Time Threshold", "The estimated owner time will change instantly if the difference is larger than this amount (in seconds) when receiving an update. Generally keep at default unless you have a very low send rate and expect large variance in your latencies.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("snapTimeThreshold"), contentSnapTimeThreshold);
                GUIContent contentSnapPositionThreshold = new GUIContent("Snap Position Threshold", "If the position is more than snapThreshold units from the target position, it will jump to the target position immediately instead of easing. Set to 0 to not use at all. In distance units.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("snapPositionThreshold"), contentSnapPositionThreshold);
                GUIContent contentSnapRotationThreshold = new GUIContent("Snap Rotation Threshold", "If the rotation is more than snapThreshold units from the target rotation, it will jump to the target rotation immediately instead of easing. Set to 0 to not use at all. In degrees.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("snapRotationThreshold"), contentSnapRotationThreshold);
                GUIContent contentSnapScaleThreshold = new GUIContent("Snap Scale Threshold", "If the scale is more than snapThreshold units from the target scale, it will jump to the target scale immediately instead of easing. Set to 0 to not use at all. In degrees.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("snapScaleThreshold"), contentSnapScaleThreshold);

                GUIContent contentSendPositionThreshold = new GUIContent("Send Position Threshold", "A synced object's position is only sent if it is off from the last sent position by more than the threshold. In distance units.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("sendPositionThreshold"), contentSendPositionThreshold);
                GUIContent contentSendRotationThreshold = new GUIContent("Send Rotation Threshold", "A synced object's rotation is only sent if it is off from the last sent rotation by more than the threshold. In degrees.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("sendRotationThreshold"), contentSendRotationThreshold);
                GUIContent contentSendScaleThreshold = new GUIContent("Send Scale Threshold", "A synced object's scale is only sent if it is off from the last sent scale by more than the threshold. In distance units.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("sendScaleThreshold"), contentSendScaleThreshold);
                GUIContent contentSendVelocityThreshold = new GUIContent("Send Velocity Threshold", "A synced object's velocity is only sent if it is off from the last sent velocity by more than the threshold. In distance per second.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("sendVelocityThreshold"), contentSendVelocityThreshold);
                GUIContent contentSendAngularVelocityThreshold = new GUIContent("Send Angular Velocity Threshold", "A synced object's angular velocity is only sent if it is off from the last sent angular velocity by more than the threshold. In degrees per second.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("sendAngularVelocityThreshold"), contentSendAngularVelocityThreshold);

                GUIContent contentReceivedPositionThreshold = new GUIContent("Received Position Threshold", "A synced object's position is only updated if it is off from the target position by more than the threshold. Set to 0 to always update. Usually keep at 0 unless you notice problems with backtracking on stops. In distance units.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("receivedPositionThreshold"), contentReceivedPositionThreshold);
                GUIContent contentReceivedRotationThreshold = new GUIContent("Received Rotation Threshold", "A synced object's rotation is only updated if it is off from the target rotation by more than the threshold. Set to 0 to always update. Usually keep at 0 unless you notice problems with backtracking on stops. In degrees.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("receivedRotationThreshold"), contentReceivedRotationThreshold);
                EditorGUI.indentLevel--;
            }

            GUIContent contentCompression = new GUIContent("Compression", "Convert floats sent over the network to Halfs, which use half as much bandwidth but are also half as precise. It'll start becoming noticeably inaccurate over ~500.");

            showCompressions = EditorGUILayout.Foldout(showCompressions, contentCompression);
            if (showCompressions)
            {
                EditorGUI.indentLevel++;
                GUIContent contentCompressPosition = new GUIContent("Compress Position", "Compress floats to save bandwidth.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("isPositionCompressed"), contentCompressPosition);
                GUIContent contentCompressRotation = new GUIContent("Compress Rotation", "Compress floats to save bandwidth.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("isRotationCompressed"), contentCompressRotation);
                GUIContent contentCompressScale = new GUIContent("Compress Scale", "Compress floats to save bandwidth.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("isScaleCompressed"), contentCompressScale);
                GUIContent contentCompressVelocity = new GUIContent("Compress Velocity", "Compress floats to save bandwidth.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("isVelocityCompressed"), contentCompressVelocity);
                GUIContent contentCompressAngularVelocity = new GUIContent("Compress Angular Velocity", "Compress floats to save bandwidth.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("isAngularVelocityCompressed"), contentCompressAngularVelocity);
                EditorGUI.indentLevel--;
            }

            GUIContent contentMiscellaneous = new GUIContent("Miscellaneous", "Miscellaneous");

            showMiscellaneous = EditorGUILayout.Foldout(showMiscellaneous, contentMiscellaneous);
            if (showMiscellaneous)
            {
                EditorGUI.indentLevel++;
                GUIContent contentSmoothAuthorityChanges = new GUIContent("Smooth Authority Changes", "Sends an extra byte if checked. Authority changes will be smoothed out.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("isSmoothingAuthorityChanges"), contentSmoothAuthorityChanges);
                GUIContent contentUseVelocityForSyncing = new GUIContent("Use Velocity Driven Syncing", "Set velocity instead of position on non-owned objects. Can be smoother but will be less accurate. Is meant to be used for racing or flying player objects but may have other uses.");
                EditorGUILayout.PropertyField(serializedObject.FindProperty("setVelocityInsteadOfPositionOnNonOwners"), contentUseVelocityForSyncing);
                if (myTarget.setVelocityInsteadOfPositionOnNonOwners == true)
                {
                    GUIContent contentMaxPositionDifference = new GUIContent("Max Position Difference", "If the difference between where it is and where it should be hits this, then it will go to location next frame. Is on an exponential scale otherwise. Only used for Velocity Driven Syncing.");
                    EditorGUILayout.PropertyField(serializedObject.FindProperty("maxPositionDifferenceForVelocitySyncing"), contentMaxPositionDifference);
                }
            }

            serializedObject.ApplyModifiedProperties();
            //EditorUtility.SetDirty(myTarget);
        }
Example #9
0
 /// <summary>
 /// Copy the SmoothSync object to a NetworkState.
 /// </summary>
 /// <param name="smoothSyncScript">The SmoothSync object</param>
 public void copyFromSmoothSync(SmoothSync smoothSyncScript)
 {
     this.smoothSync = smoothSyncScript;
     state.copyFromSmoothSync(smoothSyncScript);
 }
Example #10
0
    private void Awake()
    {
        _smoothSync     = gameObject.GetComponent <SmoothSync>();
        NetworkIdentity = gameObject.GetComponent <NetworkIdentity>();
//        DebugText = gameObject.GetComponent<TextMesh>();
    }
Example #11
0
 private void Awake()
 {
     _smoothSync = gameObject.GetComponent <SmoothSync>();
 }
Example #12
0
        public override void OnInspectorGUI()
        {
            //DrawDefaultInspector();

            SmoothSync myTarget = (SmoothSync)target;

            if (myTarget.childObjectToSync)
            {
                Color oldColor = GUI.contentColor;
                GUI.contentColor = Color.green;
                EditorGUILayout.LabelField("Syncing child", myTarget.childObjectToSync.name);
                GUI.contentColor = oldColor;
            }

            GUIContent contentWhenToUpdateTransform = new GUIContent("When to Update Transform", "Update will have smoother results but FixedUpdate might be better for physics.");

            myTarget.whenToUpdateTransform = (SmoothSync.WhenToUpdateTransform)EditorGUILayout.EnumPopup(contentWhenToUpdateTransform, myTarget.whenToUpdateTransform);
            GUIContent contentInterpolationBackTime = new GUIContent("Interpolation Back Time", "How much time in the past non-owned objects should be. This is so if you hit a latency spike, you still have a buffer of the interpolationBackTime of known States before you start extrapolating into the unknown. Increasing will make interpolation more likely to be used, decreasing will make extrapolation more likely to be used. In seconds.");

            myTarget.interpolationBackTime = EditorGUILayout.FloatField(contentInterpolationBackTime, myTarget.interpolationBackTime);
            GUIContent contentSendRate = new GUIContent("Send Rate", "How many times per second to send network updates.");

            myTarget.sendRate = EditorGUILayout.FloatField(contentSendRate, myTarget.sendRate);
            GUIContent contentTimeCorrectionSpeed = new GUIContent("Time Correction Speed", "The estimated owner time will shift by at most this amount (in seconds) when receiving an update. Lower values will be smoother but may take time to adjust to larger jumps in latency. Keep this lower than .05 unless you are having serious latency issues.");

            myTarget.timeCorrectionSpeed = EditorGUILayout.Slider(contentTimeCorrectionSpeed, myTarget.timeCorrectionSpeed, 0.0f, 5.0f);
            GUIContent contentPositionLerpSpeed = new GUIContent("Position Easing Speed", "How fast to ease to the new position on non-owned objects. 0 is never, 1 is instant.");

            myTarget.positionLerpSpeed = EditorGUILayout.Slider(contentPositionLerpSpeed, myTarget.positionLerpSpeed, 0.0f, 1.0f);
            GUIContent contentRotationLerpSpeed = new GUIContent("Rotation Easing Speed", "How fast to ease to the new rotation on non-owned objects. 0 is never, 1 is instant.");

            myTarget.rotationLerpSpeed = EditorGUILayout.Slider(contentRotationLerpSpeed, myTarget.rotationLerpSpeed, 0.0f, 1.0f);
            GUIContent contentScaleLerpSpeed = new GUIContent("Scale Easing Speed", "How fast to ease to the new scale on non-owned objects. 0 is never, 1 is instant.");

            myTarget.scaleLerpSpeed = EditorGUILayout.Slider(contentScaleLerpSpeed, myTarget.scaleLerpSpeed, 0.0f, 1.0f);
            GUIContent contentNetworkChannel = new GUIContent("Network Channel", "The channel to send network updates on.");

            myTarget.networkChannel = EditorGUILayout.IntField(contentNetworkChannel, myTarget.networkChannel);

            GUIContent contentChildObjectToSync = new GUIContent("Child Object to Sync", "Set this to sync a child object, leave blank to sync this object. Must leave one blank to sync the parent in order to sync children.");

            myTarget.childObjectToSync = (GameObject)EditorGUILayout.ObjectField(contentChildObjectToSync, myTarget.childObjectToSync, typeof(GameObject), true);

            GUIContent contentVariablesToSync = new GUIContent("Variables to Sync", "Fine tune what variables to sync.");

            showWhatToSync = EditorGUILayout.Foldout(showWhatToSync, contentVariablesToSync);
            if (showWhatToSync)
            {
                EditorGUI.indentLevel++;
                GUIContent contentSyncPosition = new GUIContent("Sync Position", "Fine tune what variables to sync.");
                myTarget.syncPosition = (SyncMode)EditorGUILayout.EnumPopup(contentSyncPosition, myTarget.syncPosition);
                GUIContent contentSyncRotation = new GUIContent("Sync Rotation", "Fine tune what variables to sync");
                myTarget.syncRotation = (SyncMode)EditorGUILayout.EnumPopup(contentSyncRotation, myTarget.syncRotation);
                GUIContent contentSyncScale = new GUIContent("Sync Scale", "Fine tune what variables to sync");
                myTarget.syncScale = (SyncMode)EditorGUILayout.EnumPopup(contentSyncScale, myTarget.syncScale);
                GUIContent contentSyncVelocity = new GUIContent("Sync Velocity", "Fine tune what variables to sync");
                myTarget.syncVelocity = (SyncMode)EditorGUILayout.EnumPopup(contentSyncVelocity, myTarget.syncVelocity);
                GUIContent contentSyncAngularVelocity = new GUIContent("Sync Angular Velocity", "Fine tune what variables to sync");
                myTarget.syncAngularVelocity = (SyncMode)EditorGUILayout.EnumPopup(contentSyncAngularVelocity, myTarget.syncAngularVelocity);
                EditorGUI.indentLevel--;
            }

            GUIContent contentExtrapolation = new GUIContent("Extrapolation", "Extrapolation is going into the unknown based on information we had in the past. Generally, you'll want extrapolation to help fill in missing information during lag spikes.");

            showExtrapolation = EditorGUILayout.Foldout(showExtrapolation, contentExtrapolation);
            if (showExtrapolation)
            {
                EditorGUI.indentLevel++;
                GUIContent contentExtrapolationMode = new GUIContent("Extrapolation Mode", "None: No extrapolation. Limited: Some extrapolation. Unlimited: Unlimited extrapolation.");
                myTarget.extrapolationMode = (SmoothSync.ExtrapolationMode)EditorGUILayout.EnumPopup(contentExtrapolationMode, myTarget.extrapolationMode);
                if (myTarget.extrapolationMode == SmoothSync.ExtrapolationMode.Limited)
                {
                    GUIContent contentUseExtrapolationTimeLimit     = new GUIContent("Use Extrapolation Time Limit", "Whether or not you want to use extrapolationTimeLimit. You can use only the extrapolationTimeLimit and save a distance check every extrapolation frame.");
                    GUIContent contentUseExtrapolationDistanceLimit = new GUIContent("Use Extrapolation Distance Limit", "Whether or not you want to use extrapolationDistanceLimit. You can use only the extrapolationTimeLimit and save a distance check every extrapolation frame.");
                    GUIContent contentExtrapolationDistanceLimit    = new GUIContent("Extrapolation Distance Limit", "How much distance into the future a non-owned object is allowed to extrapolate. In distance units.");
                    GUIContent contentExtrapolationTimeLimit        = new GUIContent("Extrapolation Time Limit", "How much time into the future a non-owned object is allowed to extrapolate. In seconds.");
                    myTarget.useExtrapolationTimeLimit     = EditorGUILayout.Toggle(contentUseExtrapolationTimeLimit, myTarget.useExtrapolationTimeLimit);
                    myTarget.extrapolationTimeLimit        = EditorGUILayout.FloatField(contentExtrapolationTimeLimit, myTarget.extrapolationTimeLimit);
                    myTarget.useExtrapolationDistanceLimit = EditorGUILayout.Toggle(contentUseExtrapolationDistanceLimit, myTarget.useExtrapolationDistanceLimit);
                    myTarget.extrapolationDistanceLimit    = EditorGUILayout.FloatField(contentExtrapolationDistanceLimit, myTarget.extrapolationDistanceLimit);
                }
                EditorGUI.indentLevel--;
            }

            GUIContent contentThresholds = new GUIContent("Thresholds", "Use thresholds to control when to send and set the transform.");

            showThresholds = EditorGUILayout.Foldout(showThresholds, contentThresholds);
            if (showThresholds)
            {
                EditorGUI.indentLevel++;
                GUIContent contentSnapTimeThreshold = new GUIContent("Snap Time Threshold", "The estimated owner time will change instantly if the difference is larger than this amount (in seconds) when receiving an update. Generally keep at default unless you have a very low send rate and expect large variance in your latencies.");
                myTarget.snapTimeThreshold = EditorGUILayout.FloatField(contentSnapTimeThreshold, myTarget.snapTimeThreshold);
                GUIContent contentSnapPositionThreshold = new GUIContent("Snap Position Threshold", "If the position is more than snapThreshold units from the target position, it will jump to the target position immediately instead of easing. Set to 0 to not use at all. In distance units.");
                myTarget.snapPositionThreshold = EditorGUILayout.FloatField(contentSnapPositionThreshold, myTarget.snapPositionThreshold);
                GUIContent contentSnapRotationThreshold = new GUIContent("Snap Rotation Threshold", "If the rotation is more than snapThreshold units from the target rotation, it will jump to the target rotation immediately instead of easing. Set to 0 to not use at all. In degrees.");
                myTarget.snapRotationThreshold = EditorGUILayout.FloatField(contentSnapRotationThreshold, myTarget.snapRotationThreshold);
                GUIContent contentSnapScaleThreshold = new GUIContent("Snap Scale Threshold", "If the scale is more than snapThreshold units from the target scale, it will jump to the target scale immediately instead of easing. Set to 0 to not use at all. In degrees.");
                myTarget.snapScaleThreshold = EditorGUILayout.FloatField(contentSnapScaleThreshold, myTarget.snapScaleThreshold);

                GUIContent contentSendPositionThreshold = new GUIContent("Send Position Threshold", "A synced object's position is only sent if it is off from the last sent position by more than the threshold. In distance units.");
                myTarget.sendPositionThreshold = EditorGUILayout.FloatField(contentSendPositionThreshold, myTarget.sendPositionThreshold);
                GUIContent contentSendRotationThreshold = new GUIContent("Send Rotation Threshold", "A synced object's rotation is only sent if it is off from the last sent rotation by more than the threshold. In degrees.");
                myTarget.sendRotationThreshold = EditorGUILayout.FloatField(contentSendRotationThreshold, myTarget.sendRotationThreshold);
                GUIContent contentSendScaleThreshold = new GUIContent("Send Scale Threshold", "A synced object's scale is only sent if it is off from the last sent scale by more than the threshold. In distance units.");
                myTarget.sendScaleThreshold = EditorGUILayout.FloatField(contentSendScaleThreshold, myTarget.sendScaleThreshold);
                GUIContent contentSendVelocityThreshold = new GUIContent("Send Velocity Threshold", "A synced object's velocity is only sent if it is off from the last sent velocity by more than the threshold. In distance per second.");
                myTarget.sendVelocityThreshold = EditorGUILayout.FloatField(contentSendVelocityThreshold, myTarget.sendVelocityThreshold);
                GUIContent contentSendAngularVelocityThreshold = new GUIContent("Send Angular Velocity Threshold", "A synced object's angular velocity is only sent if it is off from the last sent angular velocity by more than the threshold. In degrees per second.");
                myTarget.sendAngularVelocityThreshold = EditorGUILayout.FloatField(contentSendAngularVelocityThreshold, myTarget.sendAngularVelocityThreshold);

                GUIContent contentReceivedPositionThreshold = new GUIContent("Received Position Threshold", "A synced object's position is only updated if it is off from the target position by more than the threshold. Set to 0 to always update. Usually keep at 0 unless you notice problems with backtracking on stops. In distance units.");
                myTarget.receivedPositionThreshold = EditorGUILayout.FloatField(contentReceivedPositionThreshold, myTarget.receivedPositionThreshold);
                GUIContent contentReceivedRotationThreshold = new GUIContent("Received Rotation Threshold", "A synced object's rotation is only updated if it is off from the target rotation by more than the threshold. Set to 0 to always update. Usually keep at 0 unless you notice problems with backtracking on stops. In degrees.");
                myTarget.receivedRotationThreshold = EditorGUILayout.FloatField(contentReceivedRotationThreshold, myTarget.receivedRotationThreshold);
                EditorGUI.indentLevel--;
            }

            GUIContent contentCompression = new GUIContent("Compression", "Convert floats sent over the network to Halfs, which use half as much bandwidth but are also half as precise. It'll start becoming noticeably inaccurate over ~500.");

            showCompressions = EditorGUILayout.Foldout(showCompressions, contentCompression);
            if (showCompressions)
            {
                EditorGUI.indentLevel++;
                GUIContent contentCompressPosition = new GUIContent("Compress Position", "Compress floats to save bandwidth.");
                myTarget.isPositionCompressed = EditorGUILayout.Toggle(contentCompressPosition, myTarget.isPositionCompressed);
                GUIContent contentCompressRotation = new GUIContent("Compress Rotation", "Compress floats to save bandwidth.");
                myTarget.isRotationCompressed = EditorGUILayout.Toggle(contentCompressRotation, myTarget.isRotationCompressed);
                GUIContent contentCompressScale = new GUIContent("Compress Scale", "Compress floats to save bandwidth.");
                myTarget.isScaleCompressed = EditorGUILayout.Toggle(contentCompressScale, myTarget.isScaleCompressed);
                GUIContent contentCompressVelocity = new GUIContent("Compress Velocity", "Compress floats to save bandwidth.");
                myTarget.isVelocityCompressed = EditorGUILayout.Toggle(contentCompressVelocity, myTarget.isVelocityCompressed);
                GUIContent contentCompressAngularVelocity = new GUIContent("Compress Angular Velocity", "Compress floats to save bandwidth.");
                myTarget.isAngularVelocityCompressed = EditorGUILayout.Toggle(contentCompressAngularVelocity, myTarget.isAngularVelocityCompressed);
                EditorGUI.indentLevel--;
            }
            EditorUtility.SetDirty(myTarget);
        }
Example #13
0
        /// <summary>
        /// Deserialize a message from the network
        /// </summary>
        /// <remarks>
        /// Only receives what it needs. Optionally decompresses floats depending on the settings on the SmoothSync object.
        /// </remarks>
        /// <param name="writer">The Networkreader to read from </param>
        override public void Deserialize(NetworkReader reader)
        {
            byte syncInfoByte        = reader.ReadByte();
            bool syncPosition        = shouldSyncPosition(syncInfoByte);
            bool syncRotation        = shouldSyncRotation(syncInfoByte);
            bool syncVelocity        = shouldSyncVelocity(syncInfoByte);
            bool syncAngularVelocity = shouldSyncAngularVelocity(syncInfoByte);

            NetworkInstanceId netID = reader.ReadNetworkId();
            int syncIndex           = (int)reader.ReadPackedUInt32();

            state.ownerTimestamp = (int)reader.ReadPackedUInt32();
            GameObject ob = null;

            if (NetworkServer.active)
            {
                ob = NetworkServer.FindLocalObject(netID);
            }
            else
            {
                ob = ClientScene.FindLocalObject(netID);
            }

            if (!ob)
            {
                Debug.LogWarning("Could not find target for network state message.");
                return;
            }

            // Doesn't matter which SmoothSync is returned since they all have the same list.
            smoothSync = ob.GetComponent <SmoothSync>();
            // Find the correct object to sync according to syncIndex
            for (int i = 0; i < smoothSync.childObjectSmoothSyncs.Length; i++)
            {
                if (smoothSync.childObjectSmoothSyncs[i].syncIndex == syncIndex)
                {
                    smoothSync = smoothSync.childObjectSmoothSyncs[i];
                }
            }

            if (!smoothSync)
            {
                Debug.LogWarning("Could not find target for network state message.");
                return;
            }

            // read position
            if (syncPosition)
            {
                if (smoothSync.isPositionCompressed)
                {
                    if (smoothSync.isSyncingXPosition)
                    {
                        state.position.x = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingYPosition)
                    {
                        state.position.y = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingZPosition)
                    {
                        state.position.z = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                }
                else
                {
                    if (smoothSync.isSyncingXPosition)
                    {
                        state.position.x = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingYPosition)
                    {
                        state.position.y = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingZPosition)
                    {
                        state.position.z = reader.ReadSingle();
                    }
                }
            }
            else
            {
                if (smoothSync.stateCount > 0)
                {
                    state.position = smoothSync.stateBuffer[0].position;
                }
                else
                {
                    state.position = smoothSync.getPosition();
                }
            }
            // read velocity
            if (syncVelocity)
            {
                if (smoothSync.isVelocityCompressed)
                {
                    if (smoothSync.isSyncingXVelocity)
                    {
                        state.velocity.x = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingYVelocity)
                    {
                        state.velocity.y = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingZVelocity)
                    {
                        state.velocity.z = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                }
                else
                {
                    if (smoothSync.isSyncingXVelocity)
                    {
                        state.velocity.x = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingYVelocity)
                    {
                        state.velocity.y = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingZVelocity)
                    {
                        state.velocity.z = reader.ReadSingle();
                    }
                }
            }
            else
            {
                state.velocity = Vector3.zero;
            }

            // read rotation
            if (syncRotation)
            {
                Vector3 rot = new Vector3();
                if (smoothSync.isRotationCompressed)
                {
                    if (smoothSync.isSyncingXRotation)
                    {
                        rot.x = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingYRotation)
                    {
                        rot.y = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingZRotation)
                    {
                        rot.z = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    state.rotation = Quaternion.Euler(rot);
                }
                else
                {
                    if (smoothSync.isSyncingXRotation)
                    {
                        rot.x = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingYRotation)
                    {
                        rot.y = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingZRotation)
                    {
                        rot.z = reader.ReadSingle();
                    }
                    state.rotation = Quaternion.Euler(rot);
                }
            }
            else
            {
                if (smoothSync.stateCount > 0)
                {
                    state.rotation = smoothSync.stateBuffer[0].rotation;
                }
                else
                {
                    state.rotation = smoothSync.getRotation();
                }
            }
            // read anguluar velocity
            if (syncAngularVelocity)
            {
                if (smoothSync.isAngularVelocityCompressed)
                {
                    if (smoothSync.isSyncingXAngularVelocity)
                    {
                        state.angularVelocity.x = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingYAngularVelocity)
                    {
                        state.angularVelocity.y = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                    if (smoothSync.isSyncingZAngularVelocity)
                    {
                        state.angularVelocity.z = HalfHelper.Decompress(reader.ReadUInt16());
                    }
                }
                else
                {
                    if (smoothSync.isSyncingXAngularVelocity)
                    {
                        state.angularVelocity.x = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingYAngularVelocity)
                    {
                        state.angularVelocity.y = reader.ReadSingle();
                    }
                    if (smoothSync.isSyncingZAngularVelocity)
                    {
                        state.angularVelocity.z = reader.ReadSingle();
                    }
                }
            }
            else
            {
                state.angularVelocity = Vector3.zero;
            }
        }
Example #14
0
    private void CreateGunfish()
    {
        GameObject[] fishPieces = new GameObject[numberOfDivisions];
        fishPieces[0] = new GameObject(fishName);

        float fishWidth  = texture.width / gunfishSprite.pixelsPerUnit;
        float fishHeight = texture.height / gunfishSprite.pixelsPerUnit;

        LineRenderer lineFish = fishPieces[0].AddComponent <LineRenderer>();

        lineFish.positionCount = numberOfDivisions;
        lineFish.startWidth    = fishHeight;
        lineFish.endWidth      = fishHeight;
        lineFish.alignment     = LineAlignment.TransformZ;
        lineFish.material      = material;

        fishPieces[0].AddComponent <NetworkIdentity>();

        for (int i = 0; i < numberOfDivisions; i++)
        {
            if (i > 0)
            {
                fishPieces [i] = new GameObject("Fish[" + i.ToString() + "]");
            }
            fishPieces[i].layer = LayerMask.NameToLayer("Player");

            //Line Renderer
            /****************************************************************/
            LineSegment segment = fishPieces[i].AddComponent <LineSegment>();
            segment.segment = lineFish;
            segment.index   = i;
            /****************************************************************/


            //Sprite Renderer
            /****************************************************************/
            //SpriteRenderer sr = fishPieces[i].AddComponent<SpriteRenderer>();

            //sr.sprite = sprites[i];
            /****************************************************************/


            //Box Collider
            /****************************************************************/
            BoxCollider2D col            = fishPieces[i].AddComponent <BoxCollider2D>();
            float         spacing        = fishWidth / numberOfDivisions;
            float         textureSpacing = (float)texture.width / numberOfDivisions;

            int height        = texture.height;
            int textureX      = Mathf.RoundToInt(textureSpacing * i);
            int textureStartY = 0;
            int textureEndY   = height - 1;
            int textureOffset = Mathf.RoundToInt(textureSpacing / 2);

            for (int y = 0; y < height; y++)
            {
                if (texture.GetPixel(textureX + textureOffset, y).a > Mathf.Epsilon)   //Ignore invisible pixels
                {
                    textureStartY = y;
                    break;
                }
            }

            for (int y = height - 1; y >= 0; y--)
            {
                if (texture.GetPixel(textureX + textureOffset, y).a > Mathf.Epsilon)   //Ignore invisible pixels
                {
                    textureEndY = y;
                    break;
                }
            }

            if (textureEndY <= textureStartY)
            {
                textureStartY = 0;
                textureEndY   = height - 1;
            }

            float resultHeight = (textureEndY - textureStartY) / (float)height * 1.4f;

            if (resultHeight < 0.3f)
            {
                resultHeight = 0.3f;
            }
            if (resultHeight > height / gunfishSprite.pixelsPerUnit)
            {
                resultHeight = height / gunfishSprite.pixelsPerUnit;
            }

            float midpoint = height / 2f;
            float offsetY  = -(((textureEndY + textureStartY) / 2f) - midpoint) / gunfishSprite.pixelsPerUnit;

            col.size   = new Vector2(spacing, resultHeight);
            col.offset = new Vector2(0f, offsetY);
            /****************************************************************/


            //Rigidbody
            /****************************************************************/
            Rigidbody2D rb = fishPieces[i].AddComponent <Rigidbody2D>();
            rb.collisionDetectionMode = CollisionDetectionMode2D.Discrete;
            rb.mass = 1f / numberOfDivisions;
            /****************************************************************/


            if (i > 0)
            {
                //Hinge Joint
                /****************************************************************/
                HingeJoint2D joint = fishPieces [i].AddComponent <HingeJoint2D> ();

                joint.connectedBody = fishPieces [i - 1].GetComponent <Rigidbody2D> ();
                joint.anchor        = new Vector2(-spacing / 2, 0);
                joint.useLimits     = true;
                JointAngleLimits2D limits = joint.limits;
                limits.min   = 0f;
                limits.max   = 1f;
                joint.limits = limits;

                fishPieces[i].transform.position = fishPieces[0].transform.position + Vector3.right * spacing * i;
                fishPieces [i].transform.SetParent(fishPieces[0].transform);
                //fishPieces [i].transform.localScale = new Vector3 (1.5f, 1f, 1f);
                /****************************************************************/
            }
            else
            {
            }
        }

        //Gunfish Script
        /****************************************************************/
        Gunfish gf = fishPieces[0].AddComponent <Gunfish>();

        gf.ApplyVariableDefaults();
        /****************************************************************/


        //Gun
        /****************************************************************/
        GameObject gun = Instantiate(gunList[selectedGunIndex]) as GameObject;

        gun.name = gun.name.Remove(gun.name.Length - 7);
        gun.transform.SetParent(fishPieces[0].transform);
        gun.transform.localPosition = Vector3.zero;
        gun.transform.eulerAngles   = new Vector3(0f, 0f, -180f);
        /****************************************************************/

        fishPieces[0].transform.eulerAngles = new Vector3(0f, 0f, 180f); //Flip fish around cause it upside down from LineRenderer


        //Networking
        /****************************************************************/
        for (int i = 0; i < numberOfDivisions; i++)
        {
            SmoothSync smoothSync = fishPieces[0].AddComponent <SmoothSync>();
            smoothSync.childObjectToSync     = fishPieces[i];
            smoothSync.whenToUpdateTransform = SmoothSync.WhenToUpdateTransform.Update;
            smoothSync.sendRate            = 15;
            smoothSync.timeCorrectionSpeed = 0.01f;
            smoothSync.positionLerpSpeed   = 0.85f;
            smoothSync.rotationLerpSpeed   = 0.85f;
            smoothSync.scaleLerpSpeed      = 0.85f;
            smoothSync.networkChannel      = 1;

            //Variables to sync
            smoothSync.syncPosition        = SyncMode.XY;
            smoothSync.syncRotation        = SyncMode.Z;
            smoothSync.syncScale           = SyncMode.NONE;
            smoothSync.syncVelocity        = SyncMode.NONE;
            smoothSync.syncAngularVelocity = SyncMode.NONE;

            //Extrapolation
            smoothSync.extrapolationMode             = SmoothSync.ExtrapolationMode.Limited;
            smoothSync.useExtrapolationTimeLimit     = true;
            smoothSync.extrapolationTimeLimit        = 5f;
            smoothSync.useExtrapolationDistanceLimit = false;
            smoothSync.extrapolationDistanceLimit    = 20f;
        }
        /****************************************************************/

        //Create prefab of Scene instance and destroy the instance
        PrefabUtility.CreatePrefab(prefabPath + fishName + ".prefab", fishPieces[0]);

        if (!putInScene)
        {
            DestroyImmediate(fishPieces[0]);
        }
    }