Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Resolved Using CharacterController.Move / SimpleMove on clients

Discussion in 'Netcode for GameObjects' started by MrGreenish, Dec 26, 2021.

  1. MrGreenish

    MrGreenish

    Joined:
    Oct 20, 2019
    Posts:
    34
    Hi, I'm dead stuck on trying to move character controllers that are not owned by the host (server and client).

    I used the Starter Assets third person controller and modified the ThirdPersonController.cs script. It runs correct on the host but the clients doesn't move, Everything seems to be working as in the variables are updating, the speed variable and the targetDirection variable are correct on the clients. I know the method is running that is suppose to be moving the Character Controller. But the Character Controller doesn't move on anyone except the host! I tried Move and SimpleMove, same result.

    MoveController method is running on the server on Update (Line 263)
    If its not the hosts player object it will call the TellClientMoveClientRpc method (Line 276)

    Regards

    Code (CSharp):
    1.  
    2. using Unity.Netcode;
    3. using UnityEngine;
    4. #if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
    5. using UnityEngine.InputSystem;
    6. #endif
    7.  
    8. /* Note: animations are called via the controller for both the character and capsule using animator null checks
    9.  */
    10.  
    11. namespace StarterAssets
    12. {
    13.    [RequireComponent(typeof(CharacterController))]
    14. #if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
    15.    [RequireComponent(typeof(PlayerInput))]
    16. #endif
    17.    public class ThirdPersonController : NetworkBehaviour
    18.    {
    19.        [Header("Player")]
    20.        [Tooltip("Move speed of the character in m/s")]
    21.        public float MoveSpeed = 2.0f;
    22.        [Tooltip("Sprint speed of the character in m/s")]
    23.        public float SprintSpeed = 5.335f;
    24.        [Tooltip("How fast the character turns to face movement direction")]
    25.        [Range(0.0f, 0.3f)]
    26.        public float RotationSmoothTime = 0.12f;
    27.        [Tooltip("Acceleration and deceleration")]
    28.        public float SpeedChangeRate = 10.0f;
    29.  
    30.        [Space(10)]
    31.        [Tooltip("The height the player can jump")]
    32.        public float JumpHeight = 1.2f;
    33.        [Tooltip("The character uses its own gravity value. The engine default is -9.81f")]
    34.        public float Gravity = -15.0f;
    35.  
    36.        [Space(10)]
    37.        [Tooltip("Time required to pass before being able to jump again. Set to 0f to instantly jump again")]
    38.        public float JumpTimeout = 0.50f;
    39.        [Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]
    40.        public float FallTimeout = 0.15f;
    41.  
    42.        [Header("Player Grounded")]
    43.        [Tooltip("If the character is grounded or not. Not part of the CharacterController built in grounded check")]
    44.        public bool Grounded = true;
    45.        [Tooltip("Useful for rough ground")]
    46.        public float GroundedOffset = -0.14f;
    47.        [Tooltip("The radius of the grounded check. Should match the radius of the CharacterController")]
    48.        public float GroundedRadius = 0.28f;
    49.        [Tooltip("What layers the character uses as ground")]
    50.        public LayerMask GroundLayers;
    51.  
    52.        [Header("Cinemachine")]
    53.        [Tooltip("The follow target set in the Cinemachine Virtual Camera that the camera will follow")]
    54.        public GameObject CinemachineCameraTarget;
    55.        [Tooltip("How far in degrees can you move the camera up")]
    56.        public float TopClamp = 70.0f;
    57.        [Tooltip("How far in degrees can you move the camera down")]
    58.        public float BottomClamp = -30.0f;
    59.        [Tooltip("Additional degress to override the camera. Useful for fine tuning camera position when locked")]
    60.        public float CameraAngleOverride = 0.0f;
    61.        [Tooltip("For locking the camera position on all axis")]
    62.        public bool LockCameraPosition = false;
    63.  
    64.        // cinemachine
    65.        private float _cinemachineTargetYaw;
    66.        private float _cinemachineTargetPitch;
    67.  
    68.        // player
    69.        private float _speed;
    70.        private float _animationBlend;
    71.        private float _targetRotation = 0.0f;
    72.        private float _rotationVelocity;
    73.        private float _verticalVelocity;
    74.        private float _terminalVelocity = 53.0f;
    75.  
    76.        // timeout deltatime
    77.        private float _jumpTimeoutDelta;
    78.        private float _fallTimeoutDelta;
    79.  
    80.        // animation IDs
    81.        private int _animIDSpeed;
    82.        private int _animIDGrounded;
    83.        private int _animIDJump;
    84.        private int _animIDFreeFall;
    85.        private int _animIDMotionSpeed;
    86.  
    87.        private Animator _animator;
    88.        private CharacterController _controller;
    89.        private StarterAssetsInputs _input;
    90.        private GameObject _mainCamera;
    91.  
    92.        private const float _threshold = 0.01f;
    93.  
    94.        private bool _hasAnimator;
    95.  
    96.  
    97.        NetworkObject myObject;
    98.        [SerializeField] NetworkVariable<Vector3> targetDirection = new NetworkVariable<Vector3>(NetworkVariableReadPermission.Everyone);
    99.        [SerializeField] NetworkVariable<float> verticalVelocity = new NetworkVariable<float>();
    100.  
    101.        Vector3 oldTargetDirection;
    102.        float oldTargetVelocity;
    103.  
    104.        private void Awake()
    105.        {
    106.            // get a reference to our main camera
    107.            if (_mainCamera == null)
    108.            {
    109.                _mainCamera = transform.Find("PlayerCamera").gameObject;
    110.                _mainCamera.transform.parent = null;
    111.            }
    112.            //myObject = transform.GetComponentInParent<NetworkObject>();
    113.        }
    114.        //No furhter!
    115.        private void Start()
    116.        {
    117.            _hasAnimator = TryGetComponent(out _animator);
    118.            _controller = GetComponent<CharacterController>();
    119.            _input = GetComponent<StarterAssetsInputs>();
    120.  
    121.            AssignAnimationIDs();
    122.  
    123.            // reset our timeouts on start
    124.            _jumpTimeoutDelta = JumpTimeout;
    125.            _fallTimeoutDelta = FallTimeout;
    126.        }
    127.  
    128.        private void Update()
    129.        {
    130.            _hasAnimator = TryGetComponent(out _animator);
    131.          
    132.            if(IsServer)
    133.            {
    134.                Debug.Log($"Running on server for {OwnerClientId}");
    135.                // move the player
    136.                MoveController();
    137.            }
    138.            if(IsClient && IsOwner)
    139.            {
    140.                JumpAndGravity();
    141.                GroundedCheck();
    142.                if(_controller.enabled) Move();
    143.            }
    144.        }
    145.  
    146.        private void LateUpdate()
    147.        {
    148.            if(IsOwner) CameraRotation();
    149.        }
    150.  
    151.        private void AssignAnimationIDs()
    152.        {
    153.            _animIDSpeed = Animator.StringToHash("Speed");
    154.            _animIDGrounded = Animator.StringToHash("Grounded");
    155.            _animIDJump = Animator.StringToHash("Jump");
    156.            _animIDFreeFall = Animator.StringToHash("FreeFall");
    157.            _animIDMotionSpeed = Animator.StringToHash("MotionSpeed");
    158.        }
    159.  
    160.        private void GroundedCheck()
    161.        {
    162.            // set sphere position, with offset
    163.            Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z);
    164.            Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers, QueryTriggerInteraction.Ignore);
    165.  
    166.            // update animator if using character
    167.            if (_hasAnimator)
    168.            {
    169.                _animator.SetBool(_animIDGrounded, Grounded);
    170.            }
    171.        }
    172.  
    173.        private void CameraRotation()
    174.        {
    175.            // if there is an input and camera position is not fixed
    176.            if (_input.look.sqrMagnitude >= _threshold && !LockCameraPosition)
    177.            {
    178.                _cinemachineTargetYaw += _input.look.x * Time.deltaTime;
    179.                _cinemachineTargetPitch += _input.look.y * Time.deltaTime;
    180.            }
    181.  
    182.            // clamp our rotations so our values are limited 360 degrees
    183.            _cinemachineTargetYaw = ClampAngle(_cinemachineTargetYaw, float.MinValue, float.MaxValue);
    184.            _cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp);
    185.  
    186.            // Cinemachine will follow this target
    187.            CinemachineCameraTarget.transform.rotation = Quaternion.Euler(_cinemachineTargetPitch + CameraAngleOverride, _cinemachineTargetYaw, 0.0f);
    188.        }
    189.  
    190.        private void Move()
    191.        {
    192.            // set target speed based on move speed, sprint speed and if sprint is pressed
    193.            float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;
    194.  
    195.            // a simplistic acceleration and deceleration designed to be easy to remove, replace, or iterate upon
    196.  
    197.            // note: Vector2's == operator uses approximation so is not floating point error prone, and is cheaper than magnitude
    198.            // if there is no input, set the target speed to 0
    199.            if (_input.move == Vector2.zero) targetSpeed = 0.0f;
    200.  
    201.            // a reference to the players current horizontal velocity
    202.            float currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude;
    203.  
    204.            float speedOffset = 0.1f;
    205.            float inputMagnitude = _input.analogMovement ? _input.move.magnitude : 1f;
    206.  
    207.            // accelerate or decelerate to target speed
    208.            if (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset)
    209.            {
    210.                // creates curved result rather than a linear one giving a more organic speed change
    211.                // note T in Lerp is clamped, so we don't need to clamp our speed
    212.                _speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude, Time.deltaTime * SpeedChangeRate);
    213.  
    214.                // round speed to 3 decimal places
    215.                _speed = Mathf.Round(_speed * 1000f) / 1000f;
    216.            }
    217.            else
    218.            {
    219.                _speed = targetSpeed;
    220.            }
    221.            _animationBlend = Mathf.Lerp(_animationBlend, targetSpeed, Time.deltaTime * SpeedChangeRate);
    222.  
    223.            // normalise input direction
    224.            Vector3 inputDirection = new Vector3(_input.move.x, 0.0f, _input.move.y).normalized;
    225.  
    226.            // note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
    227.            // if there is a move input rotate player when the player is moving
    228.            if (_input.move != Vector2.zero)
    229.            {
    230.                _targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg + _mainCamera.transform.eulerAngles.y;
    231.                float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, _targetRotation, ref _rotationVelocity, RotationSmoothTime);
    232.  
    233.                // rotate to face input direction relative to camera position
    234.                transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);
    235.            }
    236.  
    237.  
    238.            Vector3 targetDirection = Quaternion.Euler(0.0f, _targetRotation, 0.0f) * Vector3.forward;
    239.            if(targetDirection != oldTargetDirection)
    240.             {
    241.                UpdateClientTargetDirectionServerRpc(targetDirection);
    242.                oldTargetDirection = targetDirection;
    243.             }
    244.  
    245.            // update animator if using character
    246.            if (_hasAnimator)
    247.            {
    248.                _animator.SetFloat(_animIDSpeed, _animationBlend);
    249.                _animator.SetFloat(_animIDMotionSpeed, inputMagnitude);
    250.            }
    251.        }
    252.        [ServerRpc]
    253.        public void UpdateClientTargetDirectionServerRpc(Vector3 _targetDirection)
    254.         {
    255.            targetDirection.Value = _targetDirection;
    256.            //targetDirection.SetDirty(true);
    257.         }
    258.        [ServerRpc]
    259.        public void UpdateClientVerticalVelocityServerRpc(float _verticalVelocity)
    260.         {
    261.            verticalVelocity.Value = _verticalVelocity;
    262.         }
    263.        void MoveController()
    264.        {
    265.            if(!IsOwner)
    266.             {
    267.                Debug.Log($"Trying to move {_controller.GetComponentInParent<NetworkObject>().OwnerClientId} with targetDirection : {targetDirection.Value} and verticalVelocity : {verticalVelocity.Value}.");
    268.             }
    269.            if (IsHost && IsLocalPlayer)
    270.            {
    271.                //_controller.Move(targetDirection.Value.normalized * (_speed * Time.deltaTime) + new Vector3(0.0f, verticalVelocity.Value, 0.0f) * Time.deltaTime);
    272.                _controller.SimpleMove(targetDirection.Value.normalized * (_speed /** Time.deltaTime*/) + new Vector3(0.0f, verticalVelocity.Value, 0.0f) /** Time.deltaTime*/);
    273.            }
    274.            else TellClientMoveClientRpc();
    275.        }
    276.        [ClientRpc]
    277.        public void TellClientMoveClientRpc()
    278.         {
    279.            if (_controller == null || !IsLocalPlayer) return;
    280.            Debug.Log($"TellClientMoveClientRpc: Target direction is : {targetDirection.Value.normalized}. Speed is : {_speed}. Vertical Velocity is {verticalVelocity.Value}.");
    281.            //transform.parent.name = "Player " + myObject.OwnerClientId.ToString();
    282.            _controller.SimpleMove(targetDirection.Value.normalized * (_speed /* Time.deltaTime*/) + new Vector3(0.0f, verticalVelocity.Value, 0.0f) /** Time.deltaTime*/);
    283.        }
    284.  
    285.        private void JumpAndGravity()
    286.        {
    287.            if (Grounded)
    288.            {
    289.                // reset the fall timeout timer
    290.                _fallTimeoutDelta = FallTimeout;
    291.  
    292.                // update animator if using character
    293.                if (_hasAnimator)
    294.                {
    295.                    _animator.SetBool(_animIDJump, false);
    296.                    _animator.SetBool(_animIDFreeFall, false);
    297.                }
    298.  
    299.                // stop our velocity dropping infinitely when grounded
    300.                if (_verticalVelocity < 0.0f)
    301.                {
    302.                    _verticalVelocity = -2f;
    303.                }
    304.  
    305.                // Jump
    306.                if (_input.jump && _jumpTimeoutDelta <= 0.0f)
    307.                {
    308.                    // the square root of H * -2 * G = how much velocity needed to reach desired height
    309.                    _verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);
    310.  
    311.                    // update animator if using character
    312.                    if (_hasAnimator)
    313.                    {
    314.                        _animator.SetBool(_animIDJump, true);
    315.                    }
    316.                }
    317.  
    318.                // jump timeout
    319.                if (_jumpTimeoutDelta >= 0.0f)
    320.                {
    321.                    _jumpTimeoutDelta -= Time.deltaTime;
    322.                }
    323.            }
    324.            else
    325.            {
    326.                // reset the jump timeout timer
    327.                _jumpTimeoutDelta = JumpTimeout;
    328.  
    329.                // fall timeout
    330.                if (_fallTimeoutDelta >= 0.0f)
    331.                {
    332.                    _fallTimeoutDelta -= Time.deltaTime;
    333.                }
    334.                else
    335.                {
    336.                    // update animator if using character
    337.                    if (_hasAnimator)
    338.                    {
    339.                        _animator.SetBool(_animIDFreeFall, true);
    340.                    }
    341.                }
    342.  
    343.                // if we are not grounded, do not jump
    344.                _input.jump = false;
    345.            }
    346.  
    347.            // apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)
    348.            if (_verticalVelocity < _terminalVelocity)
    349.            {
    350.                _verticalVelocity += Gravity * Time.deltaTime;
    351.            }
    352.        }
    353.  
    354.        private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
    355.        {
    356.            if (lfAngle < -360f) lfAngle += 360f;
    357.            if (lfAngle > 360f) lfAngle -= 360f;
    358.            return Mathf.Clamp(lfAngle, lfMin, lfMax);
    359.        }
    360.  
    361.        private void OnDrawGizmosSelected()
    362.        {
    363.            Color transparentGreen = new Color(0.0f, 1.0f, 0.0f, 0.35f);
    364.            Color transparentRed = new Color(1.0f, 0.0f, 0.0f, 0.35f);
    365.  
    366.            if (Grounded) Gizmos.color = transparentGreen;
    367.            else Gizmos.color = transparentRed;
    368.          
    369.            // when selected, draw a gizmo in the position of, and matching radius of, the grounded collider
    370.            Gizmos.DrawSphere(new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z), GroundedRadius);
    371.        }
    372.    }
    373. }
    374.  
     
  2. jordansean10

    jordansean10

    Joined:
    Aug 28, 2020
    Posts:
    5
    MrGreenish likes this.
  3. MrGreenish

    MrGreenish

    Joined:
    Oct 20, 2019
    Posts:
    34
    For anyone interested I changed the code so the position and rotation works on hosts and clients. Not much credit to me. I mainly learned from these tutorials and applied it to the ThirdPersonController script :
    and


    Here is my solution if anyone is interested. It seems to be working (no animator syncing). Use it, improve it.


    Code (CSharp):
    1. using Unity.Netcode;
    2. using UnityEngine;
    3. #if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
    4. using UnityEngine.InputSystem;
    5. #endif
    6.  
    7. /* Note: animations are called via the controller for both the character and capsule using animator null checks
    8. */
    9.  
    10. namespace StarterAssets
    11. {
    12.     [RequireComponent(typeof(CharacterController))]
    13. #if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
    14.     [RequireComponent(typeof(PlayerInput))]
    15. #endif
    16.     public class ThirdPersonController : NetworkBehaviour
    17.     {
    18.         [Header("Player")]
    19.         [Tooltip("Move speed of the character in m/s")]
    20.         public float MoveSpeed = 2.0f;
    21.         [Tooltip("Sprint speed of the character in m/s")]
    22.         public float SprintSpeed = 5.335f;
    23.         [Tooltip("How fast the character turns to face movement direction")]
    24.         [Range(0.0f, 0.3f)]
    25.         public float RotationSmoothTime = 0.12f;
    26.         [Tooltip("Acceleration and deceleration")]
    27.         public float SpeedChangeRate = 10.0f;
    28.  
    29.         [Space(10)]
    30.         [Tooltip("The height the player can jump")]
    31.         public float JumpHeight = 1.2f;
    32.         [Tooltip("The character uses its own gravity value. The engine default is -9.81f")]
    33.         public float Gravity = -15.0f;
    34.  
    35.         [Space(10)]
    36.         [Tooltip("Time required to pass before being able to jump again. Set to 0f to instantly jump again")]
    37.         public float JumpTimeout = 0.50f;
    38.         [Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]
    39.         public float FallTimeout = 0.15f;
    40.  
    41.         [Header("Player Grounded")]
    42.         [Tooltip("If the character is grounded or not. Not part of the CharacterController built in grounded check")]
    43.         public bool Grounded = true;
    44.         [Tooltip("Useful for rough ground")]
    45.         public float GroundedOffset = -0.14f;
    46.         [Tooltip("The radius of the grounded check. Should match the radius of the CharacterController")]
    47.         public float GroundedRadius = 0.28f;
    48.         [Tooltip("What layers the character uses as ground")]
    49.         public LayerMask GroundLayers;
    50.  
    51.         [Header("Cinemachine")]
    52.         [Tooltip("The follow target set in the Cinemachine Virtual Camera that the camera will follow")]
    53.         public GameObject CinemachineCameraTarget;
    54.         [Tooltip("How far in degrees can you move the camera up")]
    55.         public float TopClamp = 70.0f;
    56.         [Tooltip("How far in degrees can you move the camera down")]
    57.         public float BottomClamp = -30.0f;
    58.         [Tooltip("Additional degress to override the camera. Useful for fine tuning camera position when locked")]
    59.         public float CameraAngleOverride = 0.0f;
    60.         [Tooltip("For locking the camera position on all axis")]
    61.         public bool LockCameraPosition = false;
    62.  
    63.         // cinemachine
    64.         private float _cinemachineTargetYaw;
    65.         private float _cinemachineTargetPitch;
    66.  
    67.         // player
    68.         private float _speed;
    69.         private float _animationBlend;
    70.         private float _targetRotation = 0.0f;
    71.         private float _rotationVelocity;
    72.         private float _verticalVelocity;
    73.         private float _terminalVelocity = 53.0f;
    74.  
    75.         // timeout deltatime
    76.         private float _jumpTimeoutDelta;
    77.         private float _fallTimeoutDelta;
    78.  
    79.         // animation IDs
    80.         private int _animIDSpeed;
    81.         private int _animIDGrounded;
    82.         private int _animIDJump;
    83.         private int _animIDFreeFall;
    84.         private int _animIDMotionSpeed;
    85.  
    86.         private Animator _animator;
    87.         private CharacterController _controller;
    88.         private StarterAssetsInputs _input;
    89.         private GameObject _mainCamera;
    90.  
    91.         private const float _threshold = 0.01f;
    92.  
    93.         private bool _hasAnimator;
    94.  
    95.  
    96.         NetworkObject myObject;
    97.         [SerializeField] NetworkVariable<Vector3> inputDirection = new NetworkVariable<Vector3>();
    98.         [SerializeField] NetworkVariable<float> verticalVelocity = new NetworkVariable<float>();
    99.         [SerializeField] NetworkVariable<float> movementTargetSpeed = new NetworkVariable<float>();
    100.         [SerializeField] NetworkVariable<float> targetRotation = new NetworkVariable<float>();
    101.         [SerializeField] NetworkVariable<float> inputMagnitude = new NetworkVariable<float>();
    102.  
    103.         float oldSpeed;
    104.         float oldRotation;
    105.         float oldInputMagnitude;
    106.         /// <summary>
    107.         /// Old Time Since Server Start
    108.         /// </summary>
    109.         float oldTSSS = 0f;
    110.  
    111.         private void Awake()
    112.         {
    113.             // get a reference to our main camera
    114.             if (_mainCamera == null)
    115.             {
    116.                 _mainCamera = transform.Find("PlayerCamera").gameObject;
    117.                 _mainCamera.transform.parent = null;
    118.             }
    119.             //myObject = transform.GetComponentInParent<NetworkObject>();
    120.         }
    121.         //No furhter!
    122.         private void Start()
    123.         {
    124.             _hasAnimator = TryGetComponent(out _animator);
    125.             _controller = GetComponent<CharacterController>();
    126.             _input = GetComponent<StarterAssetsInputs>();
    127.  
    128.             AssignAnimationIDs();
    129.  
    130.             // reset our timeouts on start
    131.             _jumpTimeoutDelta = JumpTimeout;
    132.             _fallTimeoutDelta = FallTimeout;
    133.         }
    134.  
    135.         private void Update()
    136.         {
    137.             _hasAnimator = TryGetComponent(out _animator);
    138.            
    139.             /*if(IsServer)
    140.             {
    141.                 Debug.Log($"Running on server for {OwnerClientId}");
    142.                 // move the player
    143.                 MoveController();
    144.             }
    145.             else
    146.             {
    147.                 Debug.Log($"{OwnerClientId} IsServer : {IsServer}. IsOwnedByServer : {IsOwnedByServer}. IsClient : {IsClient}. IsSpawned : {IsSpawned}. IsLocalPlayer : {IsLocalPlayer}");
    148.             }*/
    149.             if(IsClient && IsOwner)
    150.             {
    151.                 JumpAndGravity();
    152.                 GroundedCheck();
    153.                 if(_controller.enabled) ClientInput();
    154.             }
    155.             if (IsServer)
    156.             {
    157.                 MoveController();
    158.  
    159.             }
    160.         }
    161.  
    162.         private void LateUpdate()
    163.         {
    164.             if(IsOwner) CameraRotation();
    165.         }
    166.  
    167.         private void AssignAnimationIDs()
    168.         {
    169.             _animIDSpeed = Animator.StringToHash("Speed");
    170.             _animIDGrounded = Animator.StringToHash("Grounded");
    171.             _animIDJump = Animator.StringToHash("Jump");
    172.             _animIDFreeFall = Animator.StringToHash("FreeFall");
    173.             _animIDMotionSpeed = Animator.StringToHash("MotionSpeed");
    174.         }
    175.  
    176.         private void GroundedCheck()
    177.         {
    178.             // set sphere position, with offset
    179.             Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z);
    180.             Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers, QueryTriggerInteraction.Ignore);
    181.  
    182.             // update animator if using character
    183.             if (_hasAnimator)
    184.             {
    185.                 _animator.SetBool(_animIDGrounded, Grounded);
    186.             }
    187.         }
    188.  
    189.         private void CameraRotation()
    190.         {
    191.             // if there is an input and camera position is not fixed
    192.             if (_input.look.sqrMagnitude >= _threshold && !LockCameraPosition)
    193.             {
    194.                 _cinemachineTargetYaw += _input.look.x * Time.deltaTime;
    195.                 _cinemachineTargetPitch += _input.look.y * Time.deltaTime;
    196.             }
    197.  
    198.             // clamp our rotations so our values are limited 360 degrees
    199.             _cinemachineTargetYaw = ClampAngle(_cinemachineTargetYaw, float.MinValue, float.MaxValue);
    200.             _cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp);
    201.  
    202.             // Cinemachine will follow this target
    203.             CinemachineCameraTarget.transform.rotation = Quaternion.Euler(_cinemachineTargetPitch + CameraAngleOverride, _cinemachineTargetYaw, 0.0f);
    204.         }
    205.  
    206.         private void ClientInput()
    207.         {
    208.             // set target speed based on move speed, sprint speed and if sprint is pressed
    209.             float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;
    210.  
    211.             // a simplistic acceleration and deceleration designed to be easy to remove, replace, or iterate upon
    212.  
    213.             // note: Vector2's == operator uses approximation so is not floating point error prone, and is cheaper than magnitude
    214.             // if there is no input, set the target speed to 0
    215.             if (_input.move == Vector2.zero) targetSpeed = 0.0f;
    216.  
    217.             if (targetSpeed != oldSpeed)
    218.             {
    219.                 UpdateClientSpeedServerRPC(targetSpeed);
    220.                 oldSpeed = targetSpeed;
    221.             }
    222.  
    223.             // normalise input direction
    224.             Vector3 inputDirection = new Vector3(_input.move.x, 0.0f, _input.move.y).normalized;
    225.  
    226.             float inputMagnitude = _input.analogMovement ? inputDirection.magnitude : 1f;
    227.  
    228.             if(inputMagnitude != oldInputMagnitude)
    229.             {
    230.                 UpdateClientInputMagnitudeServerRpc(inputMagnitude);
    231.                 oldInputMagnitude = inputMagnitude;
    232.             }
    233.             // note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
    234.             // if there is a move input rotate player when the player is moving
    235.             if (inputDirection != Vector3.zero)
    236.             {
    237.                 _targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg + _mainCamera.transform.eulerAngles.y;
    238.             }
    239.             // rotate to face input direction relative to camera position
    240.             if (_targetRotation != oldRotation)
    241.             {
    242.                 UpdateClientRotationServerRpc(_targetRotation);
    243.                 oldRotation = _targetRotation;
    244.             }
    245.  
    246.             _animationBlend = Mathf.Lerp(_animationBlend, movementTargetSpeed.Value, Time.deltaTime * SpeedChangeRate);
    247.             // update animator if using character
    248.             if (_hasAnimator)
    249.             {
    250.                 _animator.SetFloat(_animIDSpeed, _animationBlend);
    251.                 _animator.SetFloat(_animIDMotionSpeed, inputMagnitude);
    252.             }
    253.         }
    254.         [ServerRpc]
    255.         public void UpdateClientInputMagnitudeServerRpc(float _inputMagnitude)
    256.         {
    257.             inputMagnitude.Value = _inputMagnitude;
    258.         }
    259.         [ServerRpc]
    260.         public void UpdateClientSpeedServerRPC(float speed)
    261.         {
    262.             movementTargetSpeed.Value = speed;
    263.         }
    264.         [ServerRpc]
    265.         public void UpdateClientVerticalVelocityServerRpc(float _verticalVelocity)
    266.         {
    267.             verticalVelocity.Value = _verticalVelocity;
    268.         }
    269.         [ServerRpc]
    270.         public void UpdateClientRotationServerRpc(float _targetRotation)
    271.         {
    272.             targetRotation.Value = _targetRotation;
    273.         }
    274.         /// <summary>
    275.         /// Callled from the server on this client
    276.         /// </summary>
    277.         public void MoveController()
    278.         {
    279.             if (_controller == null) return;
    280.  
    281.             // a reference to the players current horizontal velocity
    282.             float currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude;
    283.  
    284.             float speedOffset = 0.1f;
    285.  
    286.             // accelerate or decelerate to target speed
    287.             if (currentHorizontalSpeed < movementTargetSpeed.Value - speedOffset || currentHorizontalSpeed > movementTargetSpeed.Value + speedOffset)
    288.             {
    289.                 // creates curved result rather than a linear one giving a more organic speed change
    290.                 // note T in Lerp is clamped, so we don't need to clamp our speed
    291.                 _speed = Mathf.Lerp(currentHorizontalSpeed, movementTargetSpeed.Value * inputMagnitude.Value, Time.deltaTime * SpeedChangeRate);
    292.  
    293.                 // round speed to 3 decimal places
    294.                 _speed = Mathf.Round(_speed * 1000f) / 1000f;
    295.             }
    296.             else
    297.             {
    298.                 _speed = movementTargetSpeed.Value;
    299.             }
    300.  
    301.             // Calculate movement
    302.             Vector3 targetDirection = Quaternion.Euler(0.0f, targetRotation.Value, 0.0f) * Vector3.forward;
    303.             _controller.Move(targetDirection.normalized * (movementTargetSpeed.Value) * Time.deltaTime + new Vector3(0.0f, verticalVelocity.Value, 0.0f) * Time.deltaTime);
    304.             // Calculate rotation
    305.             float blendedRotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation.Value, ref _rotationVelocity, RotationSmoothTime);
    306.             transform.rotation = Quaternion.Euler(0.0f, blendedRotation, 0.0f);
    307.         }
    308.  
    309.         private void JumpAndGravity()
    310.         {
    311.             if (Grounded)
    312.             {
    313.                 // reset the fall timeout timer
    314.                 _fallTimeoutDelta = FallTimeout;
    315.  
    316.                 // update animator if using character
    317.                 if (_hasAnimator)
    318.                 {
    319.                     _animator.SetBool(_animIDJump, false);
    320.                     _animator.SetBool(_animIDFreeFall, false);
    321.                 }
    322.  
    323.                 // stop our velocity dropping infinitely when grounded
    324.                 if (_verticalVelocity < 0.0f)
    325.                 {
    326.                     _verticalVelocity = -2f;
    327.                 }
    328.  
    329.                 // Jump
    330.                 if (_input.jump && _jumpTimeoutDelta <= 0.0f)
    331.                 {
    332.                     // the square root of H * -2 * G = how much velocity needed to reach desired height
    333.                     _verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);
    334.  
    335.                     // update animator if using character
    336.                     if (_hasAnimator)
    337.                     {
    338.                         _animator.SetBool(_animIDJump, true);
    339.                     }
    340.                 }
    341.  
    342.                 // jump timeout
    343.                 if (_jumpTimeoutDelta >= 0.0f)
    344.                 {
    345.                     _jumpTimeoutDelta -= Time.deltaTime;
    346.                 }
    347.             }
    348.             else
    349.             {
    350.                 // reset the jump timeout timer
    351.                 _jumpTimeoutDelta = JumpTimeout;
    352.  
    353.                 // fall timeout
    354.                 if (_fallTimeoutDelta >= 0.0f)
    355.                 {
    356.                     _fallTimeoutDelta -= Time.deltaTime;
    357.                 }
    358.                 else
    359.                 {
    360.                     // update animator if using character
    361.                     if (_hasAnimator)
    362.                     {
    363.                         _animator.SetBool(_animIDFreeFall, true);
    364.                     }
    365.                 }
    366.  
    367.                 // if we are not grounded, do not jump
    368.                 _input.jump = false;
    369.             }
    370.  
    371.             // apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)
    372.             if (_verticalVelocity < _terminalVelocity)
    373.             {
    374.                 _verticalVelocity += Gravity * Time.deltaTime;
    375.             }
    376.             UpdateClientVerticalVelocityServerRpc(_verticalVelocity);
    377.         }
    378.  
    379.         private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
    380.         {
    381.             if (lfAngle < -360f) lfAngle += 360f;
    382.             if (lfAngle > 360f) lfAngle -= 360f;
    383.             return Mathf.Clamp(lfAngle, lfMin, lfMax);
    384.         }
    385.  
    386.         private void OnDrawGizmosSelected()
    387.         {
    388.             Color transparentGreen = new Color(0.0f, 1.0f, 0.0f, 0.35f);
    389.             Color transparentRed = new Color(1.0f, 0.0f, 0.0f, 0.35f);
    390.  
    391.             if (Grounded) Gizmos.color = transparentGreen;
    392.             else Gizmos.color = transparentRed;
    393.            
    394.             // when selected, draw a gizmo in the position of, and matching radius of, the grounded collider
    395.             Gizmos.DrawSphere(new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z), GroundedRadius);
    396.         }
    397.     }
    398. }
     
  4. Eros96

    Eros96

    Joined:
    Sep 26, 2019
    Posts:
    28
    Hi, i am having the very same problem. Tryed with networkTransform and ClientNetworkTransform but do not fix the issue. Someone has some ideas? The ThirdPersonController scritp i am using is below.

    Regards.

    Code (CSharp):
    1. using Unity.Netcode;
    2. using UnityEngine;
    3. using UnityEngine.UIElements;
    4. #if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
    5. using UnityEngine.InputSystem;
    6. #endif
    7. /* Note: animations are called via the controller for both the character and capsule using animator null checks
    8. */
    9. namespace StarterAssets
    10. {
    11.     [RequireComponent(typeof(CharacterController))]
    12. #if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
    13.     [RequireComponent(typeof(PlayerInput))]
    14. #endif
    15.     public class ThirdPersonController : NetworkBehaviour
    16.     {
    17.         [Header("Player")]
    18.         [Tooltip("Move speed of the character in m/s")]
    19.         public float MoveSpeed = 2.0f;
    20.         [Tooltip("Sprint speed of the character in m/s")]
    21.         public float SprintSpeed = 5.335f;
    22.         [Tooltip("How fast the character turns to face movement direction")]
    23.         [Range(0.0f, 0.3f)]
    24.         public float RotationSmoothTime = 0.12f;
    25.         [Tooltip("Acceleration and deceleration")]
    26.         public float SpeedChangeRate = 10.0f;
    27.  
    28.         [Space(10)]
    29.         [Tooltip("The height the player can jump")]
    30.         public float JumpHeight = 1.2f;
    31.         [Tooltip("The character uses its own gravity value. The engine default is -9.81f")]
    32.         public float Gravity = -15.0f;
    33.  
    34.         [Space(10)]
    35.         [Tooltip("Time required to pass before being able to jump again. Set to 0f to instantly jump again")]
    36.         public float JumpTimeout = 0.50f;
    37.         [Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]
    38.         public float FallTimeout = 0.15f;
    39.  
    40.         [Header("Player Grounded")]
    41.         [Tooltip("If the character is grounded or not. Not part of the CharacterController built in grounded check")]
    42.         public bool Grounded = true;
    43.         [Tooltip("Useful for rough ground")]
    44.         public float GroundedOffset = -0.14f;
    45.         [Tooltip("The radius of the grounded check. Should match the radius of the CharacterController")]
    46.         public float GroundedRadius = 0.28f;
    47.         [Tooltip("What layers the character uses as ground")]
    48.         public LayerMask GroundLayers;
    49.  
    50.         [Header("Cinemachine")]
    51.         [Tooltip("The follow target set in the Cinemachine Virtual Camera that the camera will follow")]
    52.         public GameObject CinemachineCameraTarget;
    53.         [Tooltip("How far in degrees can you move the camera up")]
    54.         public float TopClamp = 70.0f;
    55.         [Tooltip("How far in degrees can you move the camera down")]
    56.         public float BottomClamp = -30.0f;
    57.         [Tooltip("Additional degress to override the camera. Useful for fine tuning camera position when locked")]
    58.         public float CameraAngleOverride = 0.0f;
    59.         [Tooltip("For locking the camera position on all axis")]
    60.         public bool LockCameraPosition = false;
    61.  
    62.         // cinemachine
    63.         private float _cinemachineTargetYaw;
    64.         private float _cinemachineTargetPitch;
    65.  
    66.         // player
    67.         private float _speed;
    68.         private float _animationBlend;
    69.         private float _targetRotation = 0.0f;
    70.         private float _rotationVelocity;
    71.         private float _verticalVelocity;
    72.         private float _terminalVelocity = 53.0f;
    73.  
    74.         // timeout deltatime
    75.         private float _jumpTimeoutDelta;
    76.         private float _fallTimeoutDelta;
    77.  
    78.         // animation IDs
    79.         private int _animIDSpeed;
    80.         private int _animIDGrounded;
    81.         private int _animIDJump;
    82.         private int _animIDFreeFall;
    83.         private int _animIDMotionSpeed;
    84.  
    85.         private Animator _animator;
    86.         private CharacterController _controller;
    87.         private StarterAssetsInputs _input;
    88.         private GameObject _mainCamera;
    89.  
    90.         private const float _threshold = 0.01f;
    91.  
    92.         private bool _hasAnimator;
    93.  
    94.         [SerializeField]
    95.         private NetworkVariable<Vector3> networkPositionDirection = new NetworkVariable<Vector3>();
    96.  
    97.         [SerializeField]
    98.         private NetworkVariable<Vector3> networkRotationDirection = new NetworkVariable<Vector3>();
    99.  
    100.         public override void OnNetworkSpawn()
    101.         {
    102.             base.OnNetworkSpawn();
    103.             transform.position = new Vector3(Random.Range(-5f, 5f), 0, Random.Range(1f, 5f));
    104.         }
    105.  
    106.         private void Awake()
    107.         {
    108.             // get a reference to our main camera
    109.             if (_mainCamera == null)
    110.             {
    111.                 _mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
    112.             }
    113.         }
    114.  
    115.         private void Start()
    116.         {
    117.             _hasAnimator = TryGetComponent(out _animator);
    118.             _controller = GetComponent<CharacterController>();
    119.             _input = GetComponent<StarterAssetsInputs>();
    120.  
    121.             AssignAnimationIDs();
    122.                
    123.             // reset our timeouts on start
    124.             _jumpTimeoutDelta = JumpTimeout;
    125.             _fallTimeoutDelta = FallTimeout;
    126.         }
    127.  
    128.         private void Update()
    129.         {
    130.             _hasAnimator = TryGetComponent(out _animator);
    131.            
    132.             if (IsClient && IsOwner)
    133.             {
    134.                 JumpAndGravity();
    135.                 GroundedCheck();
    136.                 Move();
    137.                 print("I am the client " + OwnerClientId + " in Update Position ");
    138.             }
    139.            
    140.             UpdateServer();
    141.         }
    142.  
    143.         private void LateUpdate()
    144.         {
    145.             CameraRotation();
    146.         }
    147.  
    148.         private void UpdateServer()
    149.         {
    150.             if (networkPositionDirection.Value != Vector3.zero)
    151.             {
    152.                 _controller.Move(networkPositionDirection.Value);
    153.             }
    154.             if (networkRotationDirection.Value != Vector3.zero)
    155.             {
    156.                 transform.rotation = Quaternion.Euler(networkRotationDirection.Value);
    157.             }
    158.         }
    159.        
    160.         [ServerRpc]
    161.         private void UpdateClientPositionServerRpc(Vector3 newPosition)
    162.         {
    163.             networkPositionDirection.Value = newPosition;
    164.  
    165.         }
    166.        
    167.         [ServerRpc]
    168.         private void UpdateClientRotationServerRpc(Vector3 newRotation)
    169.         {
    170.             networkRotationDirection.Value = newRotation;
    171.         }
    172.        
    173.         private void AssignAnimationIDs()
    174.         {
    175.             _animIDSpeed = Animator.StringToHash("Speed");
    176.             _animIDGrounded = Animator.StringToHash("Grounded");
    177.             _animIDJump = Animator.StringToHash("Jump");
    178.             _animIDFreeFall = Animator.StringToHash("FreeFall");
    179.             _animIDMotionSpeed = Animator.StringToHash("MotionSpeed");
    180.         }
    181.  
    182.         private void GroundedCheck()
    183.         {
    184.             // set sphere position, with offset
    185.             Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z);
    186.             Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers, QueryTriggerInteraction.Ignore);
    187.  
    188.             // update animator if using character
    189.             if (_hasAnimator)
    190.             {
    191.                 _animator.SetBool(_animIDGrounded, Grounded);
    192.             }
    193.         }
    194.  
    195.         private void CameraRotation()
    196.         {
    197.             // if there is an input and camera position is not fixed
    198.             if (_input.look.sqrMagnitude >= _threshold && !LockCameraPosition)
    199.             {
    200.                 _cinemachineTargetYaw += _input.look.x * Time.deltaTime;
    201.                 _cinemachineTargetPitch += _input.look.y * Time.deltaTime;
    202.             }
    203.  
    204.             // clamp our rotations so our values are limited 360 degrees
    205.             _cinemachineTargetYaw = ClampAngle(_cinemachineTargetYaw, float.MinValue, float.MaxValue);
    206.             _cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp);
    207.  
    208.             // Cinemachine will follow this target
    209.             CinemachineCameraTarget.transform.rotation = Quaternion.Euler(_cinemachineTargetPitch + CameraAngleOverride, _cinemachineTargetYaw, 0.0f);
    210.         }
    211.  
    212.         private void Move()
    213.         {
    214.             // set target speed based on move speed, sprint speed and if sprint is pressed
    215.             float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;
    216.  
    217.             // a simplistic acceleration and deceleration designed to be easy to remove, replace, or iterate upon
    218.  
    219.             // note: Vector2's == operator uses approximation so is not floating point error prone, and is cheaper than magnitude
    220.             // if there is no input, set the target speed to 0
    221.             if (_input.move == Vector2.zero) targetSpeed = 0.0f;
    222.  
    223.             // a reference to the players current horizontal velocity
    224.             float currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude;
    225.  
    226.             float speedOffset = 0.1f;
    227.             float inputMagnitude = _input.analogMovement ? _input.move.magnitude : 1f;
    228.  
    229.             // accelerate or decelerate to target speed
    230.             if (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset)
    231.             {
    232.                 // creates curved result rather than a linear one giving a more organic speed change
    233.                 // note T in Lerp is clamped, so we don't need to clamp our speed
    234.                 _speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude, Time.deltaTime * SpeedChangeRate);
    235.  
    236.                 // round speed to 3 decimal places
    237.                 _speed = Mathf.Round(_speed * 1000f) / 1000f;
    238.             }
    239.             else
    240.             {
    241.                 _speed = targetSpeed;
    242.             }
    243.             _animationBlend = Mathf.Lerp(_animationBlend, targetSpeed, Time.deltaTime * SpeedChangeRate);
    244.  
    245.             // normalise input direction
    246.             Vector3 inputDirection = new Vector3(_input.move.x, 0.0f, _input.move.y).normalized;
    247.  
    248.             // note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
    249.             // if there is a move input rotate player when the player is moving
    250.             if (_input.move != Vector2.zero)
    251.             {
    252.                 _targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg +
    253.                                   _mainCamera.transform.eulerAngles.y;
    254.                 float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, _targetRotation, ref _rotationVelocity,
    255.                     RotationSmoothTime);
    256.                
    257.                 //transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);
    258.                 UpdateClientRotationServerRpc(new Vector3(0.0f, rotation, 0.0f));
    259.             }
    260.  
    261.             Vector3 targetDirection = Quaternion.Euler(0.0f, _targetRotation, 0.0f) * Vector3.forward;
    262.             Vector3 targetMovement = targetDirection.normalized * (_speed * Time.deltaTime) +
    263.                                      new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime;
    264.            
    265.             //_controller.Move(targetMovement);
    266.             UpdateClientPositionServerRpc(targetMovement);
    267.            
    268.             // update animator if using character
    269.             if (_hasAnimator)
    270.             {
    271.                 _animator.SetFloat(_animIDSpeed, _animationBlend);
    272.                 _animator.SetFloat(_animIDMotionSpeed, inputMagnitude);
    273.             }
    274.         }
    275.  
    276.         private void JumpAndGravity()
    277.         {
    278.             if (Grounded)
    279.             {
    280.                 // reset the fall timeout timer
    281.                 _fallTimeoutDelta = FallTimeout;
    282.  
    283.                 // update animator if using character
    284.                 if (_hasAnimator)
    285.                 {
    286.                     _animator.SetBool(_animIDJump, false);
    287.                     _animator.SetBool(_animIDFreeFall, false);
    288.                 }
    289.  
    290.                 // stop our velocity dropping infinitely when grounded
    291.                 if (_verticalVelocity < 0.0f)
    292.                 {
    293.                     _verticalVelocity = -2f;
    294.                 }
    295.  
    296.                 // Jump
    297.                 if (_input.jump && _jumpTimeoutDelta <= 0.0f)
    298.                 {
    299.                     // the square root of H * -2 * G = how much velocity needed to reach desired height
    300.                     _verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);
    301.  
    302.                     // update animator if using character
    303.                     if (_hasAnimator)
    304.                     {
    305.                         _animator.SetBool(_animIDJump, true);
    306.                     }
    307.                 }
    308.  
    309.                 // jump timeout
    310.                 if (_jumpTimeoutDelta >= 0.0f)
    311.                 {
    312.                     _jumpTimeoutDelta -= Time.deltaTime;
    313.                 }
    314.             }
    315.             else
    316.             {
    317.                 // reset the jump timeout timer
    318.                 _jumpTimeoutDelta = JumpTimeout;
    319.  
    320.                 // fall timeout
    321.                 if (_fallTimeoutDelta >= 0.0f)
    322.                 {
    323.                     _fallTimeoutDelta -= Time.deltaTime;
    324.                 }
    325.                 else
    326.                 {
    327.                     // update animator if using character
    328.                     if (_hasAnimator)
    329.                     {
    330.                         _animator.SetBool(_animIDFreeFall, true);
    331.                     }
    332.                 }
    333.  
    334.                 // if we are not grounded, do not jump
    335.                 _input.jump = false;
    336.             }
    337.  
    338.             // apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)
    339.             if (_verticalVelocity < _terminalVelocity)
    340.             {
    341.                 _verticalVelocity += Gravity * Time.deltaTime;
    342.             }
    343.         }
    344.  
    345.         private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
    346.         {
    347.             if (lfAngle < -360f) lfAngle += 360f;
    348.             if (lfAngle > 360f) lfAngle -= 360f;
    349.             return Mathf.Clamp(lfAngle, lfMin, lfMax);
    350.         }
    351.  
    352.         private void OnDrawGizmosSelected()
    353.         {
    354.             Color transparentGreen = new Color(0.0f, 1.0f, 0.0f, 0.35f);
    355.             Color transparentRed = new Color(1.0f, 0.0f, 0.0f, 0.35f);
    356.  
    357.             if (Grounded) Gizmos.color = transparentGreen;
    358.             else Gizmos.color = transparentRed;
    359.            
    360.             // when selected, draw a gizmo in the position of, and matching radius of, the grounded collider
    361.             Gizmos.DrawSphere(new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z), GroundedRadius);
    362.         }
    363.     }
    364. }
     
  5. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    I think the problem may be that you are not initializing the Vector3 values of your NetworkVariables.
     
  6. Eros96

    Eros96

    Joined:
    Sep 26, 2019
    Posts:
    28
    Unfortunately initializing the networkVariables values do not solve the problem. I did this to init the values, maybe there is a better/smart way?

    Code (CSharp):
    1. [SerializeField]
    2.         private NetworkVariable<Vector3> networkPositionDirection = new NetworkVariable<Vector3>(Vector3.zero);
    3.  
    4.         [SerializeField]
    5.         private NetworkVariable<Vector3> networkRotationDirection = new NetworkVariable<Vector3>(Vector3.zero);
     
  7. Eros96

    Eros96

    Joined:
    Sep 26, 2019
    Posts:
    28
    I think i found the problem. as said there https://forum.unity.com/threads/mla...ng-actions-in-player-input-component.1232821/ In the Player Input component, the Host player has the correct Actions, but the clients have a cloned version of it, which doesn't exist. Unfortunately the code provided in the thread does not work for me.
    I am working on a slimilar solution to assign the correct Action after the player spawn, but if any of you have a better idea please share here.

    Regards
     
  8. ViktorMSc

    ViktorMSc

    Joined:
    May 28, 2022
    Posts:
    14
    Im having the same issue. Anyone has a fix?
     
  9. CodeNinja-

    CodeNinja-

    Unity Technologies

    Joined:
    Feb 22, 2023
    Posts:
    27
    For any one reading today, I would suggest disabling the player input component and enabling it in the OnNetworkSpawn override:

    Code (CSharp):
    1. public override void OnNetworkSpawn()
    2. {
    3.     if (!IsOwner) return;
    4.     GetComponent<PlayerInput>().enabled = true;
    5. }
    6.  
    And as this thread is several month old I'm tagging it as resolved (not expecting any answer but will reply if needed)!
     
  10. Khizarrahim

    Khizarrahim

    Joined:
    May 16, 2022
    Posts:
    8
    Literally having the same problem here. Finally found a thread
    Here is my code if anyone has resolved this


    using UnityEngine;
    using Unity.Netcode;
    using Cinemachine;
    using UnityEngine.Networking;
    #if ENABLE_INPUT_SYSTEM
    using UnityEngine.InputSystem;
    #endif

    /* Note: animations are called via the controller for both the character and capsule using animator null checks
    */

    namespace StarterAssets
    {
    [RequireComponent(typeof(CharacterController))]
    #if ENABLE_INPUT_SYSTEM
    [RequireComponent(typeof(PlayerInput))]
    #endif
    public class Player2 : NetworkBehaviour
    {
    [Header("Player")]
    [Tooltip("Move speed of the character in m/s")]
    public float MoveSpeed = 2.0f;

    [Tooltip("Sprint speed of the character in m/s")]
    public float SprintSpeed = 5.335f;

    [Tooltip("How fast the character turns to face movement direction")]
    [Range(0.0f, 0.3f)]
    public float RotationSmoothTime = 0.12f;

    [Tooltip("Acceleration and deceleration")]
    public float SpeedChangeRate = 10.0f;

    public AudioClip LandingAudioClip;
    public AudioClip[] FootstepAudioClips;
    [Range(0, 1)] public float FootstepAudioVolume = 0.5f;

    [Space(10)]
    [Tooltip("The height the player can jump")]
    public float JumpHeight = 1.2f;

    [Tooltip("The character uses its own gravity value. The engine default is -9.81f")]
    public float Gravity = -15.0f;

    [Space(10)]
    [Tooltip("Time required to pass before being able to jump again. Set to 0f to instantly jump again")]
    public float JumpTimeout = 0.50f;

    [Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]
    public float FallTimeout = 0.15f;

    [Header("Player Grounded")]
    [Tooltip("If the character is grounded or not. Not part of the CharacterController built in grounded check")]
    public bool Grounded = true;

    [Tooltip("Useful for rough ground")]
    public float GroundedOffset = -0.14f;

    [Tooltip("The radius of the grounded check. Should match the radius of the CharacterController")]
    public float GroundedRadius = 0.28f;

    [Tooltip("What layers the character uses as ground")]
    public LayerMask GroundLayers;

    [Header("Cinemachine")]
    [Tooltip("The follow target set in the Cinemachine Virtual Camera that the camera will follow")]
    public GameObject CinemachineCameraTarget;

    [Tooltip("How far in degrees can you move the camera up")]
    public float TopClamp = 70.0f;

    [Tooltip("How far in degrees can you move the camera down")]
    public float BottomClamp = -30.0f;

    [Tooltip("Additional degress to override the camera. Useful for fine tuning camera position when locked")]
    public float CameraAngleOverride = 0.0f;

    [Tooltip("For locking the camera position on all axis")]
    public bool LockCameraPosition = false;

    // cinemachine
    private float _cinemachineTargetYaw;
    private float _cinemachineTargetPitch;

    // player
    private float _speed;
    private float _animationBlend;
    private float _targetRotation = 0.0f;
    private float _rotationVelocity;
    private float _verticalVelocity;
    private float _terminalVelocity = 53.0f;

    // timeout deltatime
    private float _jumpTimeoutDelta;
    private float _fallTimeoutDelta;

    // animation IDs
    private int _animIDSpeed;
    private int _animIDGrounded;
    private int _animIDJump;
    private int _animIDFreeFall;
    private int _animIDMotionSpeed;

    #if ENABLE_INPUT_SYSTEM
    private PlayerInput _playerInput;
    #endif
    private Animator _animator;
    private CharacterController _controller;
    private StarterAssetsInputs _input;
    private GameObject _mainCamera;
    private CinemachineVirtualCamera _cinemachineVirtualmachine;

    private const float _threshold = 0.01f;

    private bool _hasAnimator;

    private bool IsCurrentDeviceMouse
    {
    get
    {
    #if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED
    return _playerInput.currentControlScheme == "KeyboardMouse";
    #else
    return false;
    #endif
    }
    }


    private void Awake()
    {

    // get a reference to our main camera
    if (_mainCamera == null)
    {
    _mainCamera = GameObject.FindGameObjectWithTag("AnotherCam");
    }
    if (_cinemachineVirtualmachine == null)
    {
    _cinemachineVirtualmachine = FindObjectOfType<CinemachineVirtualCamera>();
    }


    }

    private void Start()
    {
    _cinemachineTargetYaw = CinemachineCameraTarget.transform.rotation.eulerAngles.y;
    _hasAnimator = TryGetComponent(out _animator);
    _controller = GetComponent<CharacterController>();
    _input = GetComponent<StarterAssetsInputs>();


    AssignAnimationIDs();

    // reset our timeouts on start
    _jumpTimeoutDelta = JumpTimeout;
    _fallTimeoutDelta = FallTimeout;
    }


    public override void OnNetworkSpawn()
    {
    base.OnNetworkSpawn();
    if (IsClient && IsOwner)
    {
    _playerInput = GetComponent<PlayerInput>();
    _playerInput.enabled = true;
    /* _cinemachineVirtualmachine.Follow = transform.Find("PlayerCameraRoot");*/

    }

    }




    public void Update()
    {
    if (IsLocalPlayer)
    {
    _hasAnimator = TryGetComponent(out _animator);

    JumpAndGravity();
    GroundedCheck();
    Move();

    }

    }


    public void LateUpdate()
    {
    CameraRotation();
    }

    public void AssignAnimationIDs()
    {
    _animIDSpeed = Animator.StringToHash("Speed");
    _animIDGrounded = Animator.StringToHash("Grounded");
    _animIDJump = Animator.StringToHash("Jump");
    _animIDFreeFall = Animator.StringToHash("FreeFall");
    _animIDMotionSpeed = Animator.StringToHash("MotionSpeed");
    }

    public void GroundedCheck()
    {
    // set sphere position, with offset
    Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset,
    transform.position.z);
    Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers,
    QueryTriggerInteraction.Ignore);

    // update animator if using character
    if (_hasAnimator)
    {
    _animator.SetBool(_animIDGrounded, Grounded);
    }
    }

    public void CameraRotation()
    {
    // if there is an input and camera position is not fixed
    if (_input.look.sqrMagnitude >= _threshold && !LockCameraPosition)
    {
    //Don't multiply mouse input by Time.deltaTime;
    float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1.0f : Time.deltaTime;

    _cinemachineTargetYaw += _input.look.x * deltaTimeMultiplier;
    _cinemachineTargetPitch += _input.look.y * deltaTimeMultiplier;
    }

    // clamp our rotations so our values are limited 360 degrees
    _cinemachineTargetYaw = ClampAngle(_cinemachineTargetYaw, float.MinValue, float.MaxValue);
    _cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp);

    // Cinemachine will follow this target
    CinemachineCameraTarget.transform.rotation = Quaternion.Euler(_cinemachineTargetPitch + CameraAngleOverride,
    _cinemachineTargetYaw, 0.0f);
    }

    public void Move()
    {
    // set target speed based on move speed, sprint speed and if sprint is pressed
    float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;

    // a simplistic acceleration and deceleration designed to be easy to remove, replace, or iterate upon

    // note: Vector2's == operator uses approximation so is not floating point error prone, and is cheaper than magnitude
    // if there is no input, set the target speed to 0
    if (_input.move == Vector2.zero) targetSpeed = 0.0f;

    // a reference to the players current horizontal velocity
    float currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude;

    float speedOffset = 0.1f;
    float inputMagnitude = _input.analogMovement ? _input.move.magnitude : 1f;

    // accelerate or decelerate to target speed
    if (currentHorizontalSpeed < targetSpeed - speedOffset ||
    currentHorizontalSpeed > targetSpeed + speedOffset)
    {
    // creates curved result rather than a linear one giving a more organic speed change
    // note T in Lerp is clamped, so we don't need to clamp our speed
    _speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude,
    Time.deltaTime * SpeedChangeRate);

    // round speed to 3 decimal places
    _speed = Mathf.Round(_speed * 1000f) / 1000f;
    }
    else
    {
    _speed = targetSpeed;
    }

    _animationBlend = Mathf.Lerp(_animationBlend, targetSpeed, Time.deltaTime * SpeedChangeRate);
    if (_animationBlend < 0.01f) _animationBlend = 0f;

    // normalise input direction
    Vector3 inputDirection = new Vector3(_input.move.x, 0.0f, _input.move.y).normalized;

    // note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
    // if there is a move input rotate player when the player is moving
    if (_input.move != Vector2.zero)
    {
    _targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.y) * Mathf.Rad2Deg + _mainCamera.transform.eulerAngles.y;
    float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, _targetRotation, ref _rotationVelocity,RotationSmoothTime);

    // rotate to face input direction relative to camera position
    transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);
    }


    Vector3 targetDirection = Quaternion.Euler(0.0f, _targetRotation, 0.0f) * Vector3.forward;

    // move the player
    _controller.Move(targetDirection.normalized * (_speed * Time.deltaTime) +
    new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime);

    // update animator if using character
    if (_hasAnimator)
    {
    _animator.SetFloat(_animIDSpeed, _animationBlend);
    _animator.SetFloat(_animIDMotionSpeed, inputMagnitude);
    }
    }

    public void JumpAndGravity()
    {
    if (Grounded)
    {
    // reset the fall timeout timer
    _fallTimeoutDelta = FallTimeout;

    // update animator if using character
    if (_hasAnimator)
    {
    _animator.SetBool(_animIDJump, false);
    _animator.SetBool(_animIDFreeFall, false);
    }

    // stop our velocity dropping infinitely when grounded
    if (_verticalVelocity < 0.0f)
    {
    _verticalVelocity = -2f;
    }

    // Jump
    if (_input.jump && _jumpTimeoutDelta <= 0.0f)
    {
    // the square root of H * -2 * G = how much velocity needed to reach desired height
    _verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);

    // update animator if using character
    if (_hasAnimator)
    {
    _animator.SetBool(_animIDJump, true);
    }
    }

    // jump timeout
    if (_jumpTimeoutDelta >= 0.0f)
    {
    _jumpTimeoutDelta -= Time.deltaTime;
    }
    }
    else
    {
    // reset the jump timeout timer
    _jumpTimeoutDelta = JumpTimeout;

    // fall timeout
    if (_fallTimeoutDelta >= 0.0f)
    {
    _fallTimeoutDelta -= Time.deltaTime;
    }
    else
    {
    // update animator if using character
    if (_hasAnimator)
    {
    _animator.SetBool(_animIDFreeFall, true);
    }
    }

    // if we are not grounded, do not jump
    _input.jump = false;
    }

    // apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)
    if (_verticalVelocity < _terminalVelocity)
    {
    _verticalVelocity += Gravity * Time.deltaTime;
    }
    }

    private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
    {
    if (lfAngle < -360f) lfAngle += 360f;
    if (lfAngle > 360f) lfAngle -= 360f;
    return Mathf.Clamp(lfAngle, lfMin, lfMax);
    }

    private void OnDrawGizmosSelected()
    {
    Color transparentGreen = new Color(0.0f, 1.0f, 0.0f, 0.35f);
    Color transparentRed = new Color(1.0f, 0.0f, 0.0f, 0.35f);

    if (Grounded) Gizmos.color = transparentGreen;
    else Gizmos.color = transparentRed;

    // when selected, draw a gizmo in the position of, and matching radius of, the grounded collider
    Gizmos.DrawSphere(
    new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z),
    GroundedRadius);
    }

    private void OnFootstep(AnimationEvent animationEvent)
    {
    if (animationEvent.animatorClipInfo.weight > 0.5f)
    {
    if (FootstepAudioClips.Length > 0)
    {
    var index = Random.Range(0, FootstepAudioClips.Length);
    AudioSource.PlayClipAtPoint(FootstepAudioClips[index], transform.TransformPoint(_controller.center), FootstepAudioVolume);
    }
    }
    }

    private void OnLand(AnimationEvent animationEvent)
    {
    if (animationEvent.animatorClipInfo.weight > 0.5f)
    {
    AudioSource.PlayClipAtPoint(LandingAudioClip, transform.TransformPoint(_controller.center), FootstepAudioVolume);
    }
    }
    }
    }
     
  11. CodeNinja-

    CodeNinja-

    Unity Technologies

    Joined:
    Feb 22, 2023
    Posts:
    27
    Hi @Khizarrahim,

    I copy/pasted your code in a test project and it's working fine, no errors prompted and I am able to play at host side as at client side (just having the S key moving forward the player and not backward).

    I used Unity version 2021.3.19f1, what's yours?
    Would you mind sharing the project so I can reproduce the problem?

    By the way, for sharing code prefer using the insert code button instead of pasting it in text, it will help readability:
    upload_2023-6-1_16-12-2.png
     
  12. Khizarrahim

    Khizarrahim

    Joined:
    May 16, 2022
    Posts:
    8
    The error i am getting is on this line
    _targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.y) * Mathf.Rad2Deg + _mainCamera.transform.eulerAngles.y;
    MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object. StarterAssets.ThirdPersonController.Move () (at Assets/StarterAssets/ThirdPersonController/Scripts/ThirdPersonController.cs:287) StarterAssets.ThirdPersonController.Update () (at Assets/StarterAssets/ThirdPersonController/Scripts/ThirdPersonController.cs:186)
    This is when i use the client
    The unity version is 2021.3.21f1

    Here is the drive ink to import my project. It would help me alot if this gets fixed.
    https://drive.google.com/drive/folders/1Q_d8eZV6XdSj-hGDnwNNG9cQSQyvewde?usp=sharing
     
  13. MrGreenish

    MrGreenish

    Joined:
    Oct 20, 2019
    Posts:
    34
    Can you try changing the _mainCamera.transform.eulerAngles.y to Camera.main.transform.eulerAngles.y
     
  14. Khizarrahim

    Khizarrahim

    Joined:
    May 16, 2022
    Posts:
    8
    This actually worked but now i am having problem with the cinemachine camera follow

    Code (CSharp):
    1.  public override void OnNetworkSpawn()
    2.         {
    3.             base.OnNetworkSpawn();
    4.             if (IsClient && IsOwner)
    5.             {
    6.                 _playerInput = GetComponent<PlayerInput>();
    7.                 _playerInput.enabled = true;
    8.                 _cinemachineVirtualmachine.Follow = transform.Find("PlayerCameraRoot");
    9.  
    10.             }
    11.  
    12.         }
    I wrote this cinemachine line it works for host but gives the following error in client
    NullReferenceException: Object reference not set to an instance of an object StarterAssets.ThirdPersonController.OnNetworkSpawn () (at Assets/StarterAssets/ThirdPersonController/Scripts/ThirdPersonController.cs:169)

    Any more suggestion
    Thankyouu so much for the replies
     
  15. MrGreenish

    MrGreenish

    Joined:
    Oct 20, 2019
    Posts:
    34
    Try changing the Awake method. I commented out the if-statement so it always assigns the variables on Awake
    Code (CSharp):
    1. private void Awake()
    2. {
    3.  
    4. // get a reference to our main camera
    5. /*if (_mainCamera == null)
    6. {*/
    7. _mainCamera = GameObject.FindGameObjectWithTag("AnotherCam");
    8. //}
    9. /*if (_cinemachineVirtualmachine == null)
    10. {*/
    11. _cinemachineVirtualmachine = FindObjectOfType<CinemachineVirtualCamera>();
    12. //}
    13.  
    14.  
    15. }
     
  16. Khizarrahim

    Khizarrahim

    Joined:
    May 16, 2022
    Posts:
    8
    Still error
    NullReferenceException: Object reference not set to an instance of an object StarterAssets.ThirdPersonController.OnNetworkSpawn () (at Assets/StarterAssets/ThirdPersonController/Scripts/ThirdPersonController.cs:169)
    _cinemachineVirtualmachine.Follow = transform.Find("PlayerCameraRoot");
     
  17. CodeNinja-

    CodeNinja-

    Unity Technologies

    Joined:
    Feb 22, 2023
    Posts:
    27
    I downloaded your project and I don't have same errors than you (but it's not working anyways).

    I was wondering, why do you have two scripts for the player? There is a ThirdPersonController.cs and a Player2.cs scripts, both with same logic. You should only have one script for those and I would use
    if (!IsOwner) return;
    instead of
    if (IsClient && IsOwner)
    or
    if (IsLocalPlayer)
    :

    Code (CSharp):
    1. public override void OnNetworkSpawn()
    2.         {
    3.             if (!IsOwner) return;
    4.             GetComponent<PlayerInput>().enabled = true;
    5.         }
    6.  
    7. //[...]
    8.  
    9. private void Update()
    10.         {
    11.             if (!IsOwner) { return; }
    12.             _hasAnimator = TryGetComponent(out _animator);
    13.  
    14.             JumpAndGravity();
    15.             GroundedCheck();
    16.             Move();
    17.         }
    I wouldn't use a second prefab for the other player (Nova) but change the visual at network spawn using an id or something like that.

    I think the camera problem was due to the missing "AnotherCamera" tag. Using "MainCamera" will work for both players as they'll take their own main camera.

    For starting, I would recommend having only one script for all players, make it work, and then add your player selection logic, prefabs, etc.

    Hope this helps!
     
    Last edited: Jun 2, 2023
  18. Khizarrahim

    Khizarrahim

    Joined:
    May 16, 2022
    Posts:
    8
    I made the other script to just test out
    Should i have seperate Main Cameras For Both? And Insert them inside the Prefabs itself?
     
  19. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    437
    In general, cameras should not be in the prefabs of your networked objects. You should isntead spawn and set them up only for the local player, at runtime.
    the same applies for all other components that are related to the local player (UI, input systems, etc...) otherwise you'll end up controlling multiple players with the same input, having problems with players not moving, UI resizing incorrectly, etc...


    Have you already had a look at the examples in the official ClientDriven sample? (Download here)

    It show how to use the third person controller asset in a multiplayer environment for movement and picking objects up.

    As concerns the other issues discussed in this thread, they're on the same line of the ones solved in this thread . tl;dr: ensure your player prefab has no input script enabled by default, and that you enable it only on the local Player when it spawns.
    You also need to ensure that you're using the ClientNetworkTransform script displayed at the bottom of this page to make the server accept the player movement coming from the clients.

    Here's more info about how to do so with the Third Person Controller that comes with the standards assets.

    Does this help?
     
    CodeNinja- likes this.
  20. Khizarrahim

    Khizarrahim

    Joined:
    May 16, 2022
    Posts:
    8
    Still havent figured out the cinemachine issue
    I have just 1 Main Camera ( Contains Cinemachine Brain) and PlayerFollowCamera (That contains CinemachineVirtualCemra)
    It just doesn't assigns the camera to the client only works for host.
    Or sometimes even uses the hosts camera
    I am so confused