Search Unity

  1. The 2022.1 beta is now available for testing. To find out what's new, have a look at our 2022.1 beta blog post.
    Dismiss Notice

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:
    26
    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:
    3
    MrGreenish likes this.
  3. MrGreenish

    MrGreenish

    Joined:
    Oct 20, 2019
    Posts:
    26
    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. }
     
unityunity