Search Unity

Character dropping through world

Discussion in 'Physics' started by rycornx, Aug 3, 2020.

  1. rycornx

    rycornx

    Joined:
    Oct 4, 2017
    Posts:
    125
    I'm using a slightly modified/ simplified version of the 3rd person controller from the 3d game kit, and for some reason occasionally when my character turns they just teleport downwards and go under the ground. whats going on? only trying to get the locomotion working right now
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.XR.WSA;

    5. [RequireComponent(typeof(CharacterController))]
    6. [RequireComponent(typeof(Animator))]
    7. public class PlayerControllerSimple : MonoBehaviour
    8. {

    9. protected static PlayerControllerSimple s_Instance;
    10. public static PlayerControllerSimple instance { get { return s_Instance; } }


    11. public float maxForwardSpeed = 8f; // How fast Ellen can run.
    12. public float gravity = 20f; // How fast Ellen accelerates downwards when airborne.
    13. public float jumpSpeed = 10f; // How fast Ellen takes off when jumping.
    14. public float minTurnSpeed = 400f; // How fast Ellen turns when moving at maximum speed.
    15. public float maxTurnSpeed = 1200f; // How fast Ellen turns when stationary.
    16. public float idleTimeout = 5f; // How long before Ellen starts considering random idles.
    17. public bool canAttack; // Whether or not Ellen can swing her staff.

    18. public CameraSettings cameraSettings; // Reference used to determine the camera's direction.

    19. protected bool m_IsGrounded = true; // Whether or not Ellen is currently standing on the ground.
    20. protected bool m_PreviouslyGrounded = true; // Whether or not Ellen was standing on the ground last frame.
    21. protected bool m_ReadyToJump; // Whether or not the input state and Ellen are correct to allow jumping.
    22. protected float m_DesiredForwardSpeed; // How fast Ellen aims be going along the ground based on input.
    23. public float m_ForwardSpeed; // How fast Ellen is currently going along the ground.
    24. protected float m_VerticalSpeed; // How fast Ellen is currently moving up or down.
    25. public PlayerInput m_Input; // Reference used to determine how Ellen should move.
    26. protected CharacterController m_CharCtrl; // Reference used to actually move Ellen.
    27. protected Animator m_Animator; // Reference used to make decisions based on Ellen's current animation and to set parameters.
    28. protected Quaternion m_TargetRotation; // What rotation Ellen is aiming to have based on input.
    29. protected float m_AngleDiff; // Angle in degrees between Ellen's current rotation and her target rotation.
    30. protected Collider[] m_OverlapResult = new Collider[8]; // Used to cache colliders that are near Ellen.
    31. protected bool m_InAttack; // Whether Ellen is currently in the middle of a melee attack.
    32. protected bool m_InCombo; // Whether Ellen is currently in the middle of her melee combo.
    33. protected Renderer[] m_Renderers; // References used to make sure Renderers are reset properly.
    34. protected float m_IdleTimer; // Used to count up to Ellen considering a random idle.

    35. // These constants are used to ensure Ellen moves and behaves properly.
    36. // It is advised you don't change them without fully understanding what they do in code.
    37. const float k_AirborneTurnSpeedProportion = 5.4f;
    38. const float k_GroundedRayDistance = 1f;
    39. const float k_JumpAbortSpeed = 10f;
    40. const float k_MinEnemyDotCoeff = 0.2f;
    41. const float k_InverseOneEighty = 1f / 180f;
    42. const float k_StickingGravityProportion = 0.3f;
    43. const float k_GroundAcceleration = 20f;
    44. const float k_GroundDeceleration = 25f;

    45. // Parameters

    46. readonly int m_HashAirborneVerticalSpeed = Animator.StringToHash("AirborneVerticalSpeed");
    47. readonly int m_HashForwardSpeed = Animator.StringToHash("ForwardSpeed");
    48. readonly int m_HashAngleDeltaRad = Animator.StringToHash("AngleDeltaRad");
    49. readonly int m_HashTimeoutToIdle = Animator.StringToHash("TimeoutToIdle");
    50. readonly int m_HashGrounded = Animator.StringToHash("Grounded");
    51. readonly int m_HashInputDetected = Animator.StringToHash("InputDetected");
    52. readonly int m_HashMeleeAttack = Animator.StringToHash("MeleeAttack");
    53. readonly int m_HashStateTime = Animator.StringToHash("StateTime");
    54. readonly int m_HashFootFall = Animator.StringToHash("FootFall");

    55. // States
    56. readonly int m_HashLocomotion = Animator.StringToHash("Locomotion");
    57. readonly int m_HashAirborne = Animator.StringToHash("Airborne");
    58. readonly int m_HashLanding = Animator.StringToHash("Landing"); // Also a parameter.
    59. readonly int m_HashEllenCombo1 = Animator.StringToHash("EllenCombo1");
    60. readonly int m_HashEllenCombo2 = Animator.StringToHash("EllenCombo2");
    61. readonly int m_HashEllenCombo3 = Animator.StringToHash("EllenCombo3");
    62. readonly int m_HashEllenCombo4 = Animator.StringToHash("EllenCombo4");
    63. readonly int m_HashEllenDeath = Animator.StringToHash("EllenDeath");

    64. public Vector2 inputnumbers;

    65. // Tags
    66. readonly int m_HashBlockInput = Animator.StringToHash("BlockInput");

    67. protected bool IsMoveInput
    68. {
    69. get { return !Mathf.Approximately(m_Input.MoveInput.sqrMagnitude, 0f); }
    70. }

    71. public void SetCanAttack(bool canAttack)
    72. {
    73. this.canAttack = canAttack;
    74. }

    75. // Called automatically by Unity when the script is first added to a gameobject or is reset from the context menu.
    76. void Reset()
    77. {
    78. cameraSettings = FindObjectOfType<CameraSettings>();

    79. if (cameraSettings != null)
    80. {
    81. if (cameraSettings.follow == null)
    82. cameraSettings.follow = transform;

    83. if (cameraSettings.lookAt == null)
    84. cameraSettings.follow = transform.Find("HeadTarget");
    85. }
    86. }

    87. // Called automatically by Unity when the script first exists in the scene.
    88. void Awake()
    89. {
    90. m_Input = GetComponent<PlayerInput>();
    91. m_Animator = GetComponent<Animator>();
    92. m_CharCtrl = GetComponent<CharacterController>();

    93. s_Instance = this;
    94. }

    95. // Called automatically by Unity once every Physics step.
    96. void FixedUpdate()
    97. {

    98. //if (m_Input.Attack && canAttack)
    99. // m_Animator.SetTrigger(m_HashMeleeAttack);

    100. CalculateForwardMovement();
    101. CalculateVerticalMovement();

    102. SetTargetRotation();

    103. if (IsMoveInput)
    104. UpdateOrientation();

    105. TimeoutToIdle();
    106. }

    107. // Called each physics step.
    108. void CalculateForwardMovement()
    109. {
    110. // Cache the move input and cap it's magnitude at 1.
    111. Vector2 moveInput = m_Input.MoveInput;
    112. if (moveInput.sqrMagnitude > 1f)
    113. moveInput.Normalize();

    114. inputnumbers = m_Input.MoveInput;
    115. // Calculate the speed intended by input.
    116. m_DesiredForwardSpeed = moveInput.magnitude * maxForwardSpeed;

    117. // Determine change to speed based on whether there is currently any move input.
    118. float acceleration = IsMoveInput ? k_GroundAcceleration : k_GroundDeceleration;

    119. // Adjust the forward speed towards the desired speed.
    120. m_ForwardSpeed = Mathf.MoveTowards(m_ForwardSpeed, m_DesiredForwardSpeed, acceleration * Time.deltaTime);

    121. // Set the animator parameter to control what animation is being played.
    122. m_Animator.SetFloat("ForwardSpeed", m_ForwardSpeed);
    123. }

    124. // Called each physics step.
    125. void CalculateVerticalMovement()
    126. {
    127. // If jump is not currently held and Ellen is on the ground then she is ready to jump.
    128. if (!m_Input.JumpInput && m_IsGrounded)
    129. m_ReadyToJump = true;

    130. if (m_IsGrounded)
    131. {
    132. // When grounded we apply a slight negative vertical speed to make Ellen "stick" to the ground.
    133. m_VerticalSpeed = -gravity * k_StickingGravityProportion;

    134. // If jump is held, Ellen is ready to jump and not currently in the middle of a melee combo...
    135. if (m_Input.JumpInput && m_ReadyToJump && !m_InCombo)
    136. {
    137. // ... then override the previously set vertical speed and make sure she cannot jump again.
    138. m_VerticalSpeed = jumpSpeed;
    139. m_IsGrounded = false;
    140. m_ReadyToJump = false;
    141. }
    142. }
    143. else
    144. {
    145. // If Ellen is airborne, the jump button is not held and Ellen is currently moving upwards...
    146. if (!m_Input.JumpInput && m_VerticalSpeed > 0.0f)
    147. {
    148. // ... decrease Ellen's vertical speed.
    149. // This is what causes holding jump to jump higher that tapping jump.
    150. m_VerticalSpeed -= k_JumpAbortSpeed * Time.deltaTime;
    151. }

    152. // If a jump is approximately peaking, make it absolute.
    153. if (Mathf.Approximately(m_VerticalSpeed, 0f))
    154. {
    155. m_VerticalSpeed = 0f;
    156. }

    157. // If Ellen is airborne, apply gravity.
    158. m_VerticalSpeed -= gravity * Time.deltaTime;
    159. }
    160. }

    161. // Called each physics step to set the rotation Ellen is aiming to have.
    162. void SetTargetRotation()
    163. {
    164. // Create three variables, move input local to the player, flattened forward direction of the camera and a local target rotation.
    165. Vector2 moveInput = m_Input.MoveInput;
    166. Vector3 localMovementDirection = new Vector3(moveInput.x, 0f, moveInput.y).normalized;

    167. Vector3 forward = Quaternion.Euler(0f, cameraSettings.Current.m_XAxis.Value, 0f) * Vector3.forward;
    168. forward.y = 0f;
    169. forward.Normalize();

    170. Quaternion targetRotation;

    171. // If the local movement direction is the opposite of forward then the target rotation should be towards the camera.
    172. if (Mathf.Approximately(Vector3.Dot(localMovementDirection, Vector3.forward), -1.0f))
    173. {
    174. targetRotation = Quaternion.LookRotation(-forward);
    175. }
    176. else
    177. {
    178. // Otherwise the rotation should be the offset of the input from the camera's forward.
    179. Quaternion cameraToInputOffset = Quaternion.FromToRotation(Vector3.forward, localMovementDirection);
    180. targetRotation = Quaternion.LookRotation(cameraToInputOffset * forward);
    181. }

    182. // The desired forward direction of Ellen.
    183. Vector3 resultingForward = targetRotation * Vector3.forward;

    184. // If attacking try to orient to close enemies.
    185. if (m_InAttack)
    186. {
    187. // Find all the enemies in the local area.
    188. Vector3 centre = transform.position + transform.forward * 2.0f + transform.up;
    189. Vector3 halfExtents = new Vector3(3.0f, 1.0f, 2.0f);
    190. int layerMask = 1 << LayerMask.NameToLayer("Enemy");
    191. int count = Physics.OverlapBoxNonAlloc(centre, halfExtents, m_OverlapResult, targetRotation, layerMask);

    192. // Go through all the enemies in the local area...
    193. float closestDot = 0.0f;
    194. Vector3 closestForward = Vector3.zero;
    195. int closest = -1;

    196. for (int i = 0; i < count; ++i)
    197. {
    198. // ... and for each get a vector from the player to the enemy.
    199. Vector3 playerToEnemy = m_OverlapResult.transform.position - transform.position;
      [*] playerToEnemy.y = 0;
      [*] playerToEnemy.Normalize();
      [*]

      [*] // Find the dot product between the direction the player wants to go and the direction to the enemy.
      [*] // This will be larger the closer to Ellen's desired direction the direction to the enemy is.
      [*] float d = Vector3.Dot(resultingForward, playerToEnemy);
      [*]

      [*] // Store the closest enemy.
      [*] if (d > k_MinEnemyDotCoeff && d > closestDot)
      [*] {
      [*] closestForward = playerToEnemy;
      [*] closestDot = d;
      [*] closest = i;
      [*] }
      [*] }
      [*]

      [*] // If there is a close enemy...
      [*] if (closest != -1)
      [*] {
      [*] // The desired forward is the direction to the closest enemy.
      [*] resultingForward = closestForward;
      [*]

      [*] // We also directly set the rotation, as we want snappy fight and orientation isn't updated in the UpdateOrientation function during an atatck.
      [*] transform.rotation = Quaternion.LookRotation(resultingForward);
      [*] }
      [*] }
      [*]

      [*] // Find the difference between the current rotation of the player and the desired rotation of the player in radians.
      [*] float angleCurrent = Mathf.Atan2(transform.forward.x, transform.forward.z) * Mathf.Rad2Deg;
      [*] float targetAngle = Mathf.Atan2(resultingForward.x, resultingForward.z) * Mathf.Rad2Deg;
      [*]

      [*] m_AngleDiff = Mathf.DeltaAngle(angleCurrent, targetAngle);
      [*] m_TargetRotation = targetRotation;
      [*] }
      [*]

      [*]

      [*] // Called each physics step after SetTargetRotation if there is move input and Ellen is in the correct animator state according to IsOrientationUpdated.
      [*] void UpdateOrientation()
      [*] {
      [*] m_Animator.SetFloat("AngleDeltaRad", m_AngleDiff * Mathf.Deg2Rad);
      [*]

      [*] Vector3 localInput = new Vector3(m_Input.MoveInput.x, 0f, m_Input.MoveInput.y);
      [*] float groundedTurnSpeed = Mathf.Lerp(maxTurnSpeed, minTurnSpeed, m_ForwardSpeed / m_DesiredForwardSpeed);
      [*] float actualTurnSpeed = m_IsGrounded ? groundedTurnSpeed : Vector3.Angle(transform.forward, localInput) * k_InverseOneEighty * k_AirborneTurnSpeedProportion * groundedTurnSpeed;
      [*] m_TargetRotation = Quaternion.RotateTowards(transform.rotation, m_TargetRotation, actualTurnSpeed * Time.deltaTime);
      [*]

      [*] transform.rotation = m_TargetRotation;
      [*] }
      [*]

      [*] // Called each physics step to count up to the point where Ellen considers a random idle.
      [*] void TimeoutToIdle()
      [*] {
      [*] bool inputDetected = IsMoveInput || m_Input.Attack || m_Input.JumpInput;
      [*] if (m_IsGrounded && !inputDetected)
      [*] {
      [*] m_IdleTimer += Time.deltaTime;
      [*]

      [*] if (m_IdleTimer >= idleTimeout)
      [*] {
      [*] m_IdleTimer = 0f;
      [*] m_Animator.SetTrigger(m_HashTimeoutToIdle);
      [*] }
      [*] }
      [*] else
      [*] {
      [*] m_IdleTimer = 0f;
      [*] m_Animator.ResetTrigger(m_HashTimeoutToIdle);
      [*] }
      [*]

      [*] m_Animator.SetBool(m_HashInputDetected, inputDetected);
      [*] }
      [*]

      [*] // Called each physics step (so long as the Animator component is set to Animate Physics) after FixedUpdate to override root motion.
      [*] void OnAnimatorMove()
      [*] {
      [*] Vector3 movement;
      [*]

      [*] // If Ellen is on the ground...
      [*] if (m_IsGrounded)
      [*] {
      [*] // ... raycast into the ground...
      [*] RaycastHit hit;
      [*] Ray ray = new Ray(transform.position + Vector3.up * k_GroundedRayDistance * 0.5f, -Vector3.up);
      [*] if (Physics.Raycast(ray, out hit, k_GroundedRayDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
      [*] {
      [*] // ... and get the movement of the root motion rotated to lie along the plane of the ground.
      [*] movement = Vector3.ProjectOnPlane(m_Animator.deltaPosition, hit.normal);
      [*]

      [*] // Also store the current walking surface so the correct audio is played.
      [*] Renderer groundRenderer = hit.collider.GetComponentInChildren<Renderer>();
      [*] }
      [*] else
      [*] {
      [*] // If no ground is hit just get the movement as the root motion.
      [*] // Theoretically this should rarely happen as when grounded the ray should always hit.
      [*] movement = m_Animator.deltaPosition;
      [*]

      [*] }
      [*] }
      [*] else
      [*] {
      [*] // If not grounded the movement is just in the forward direction.
      [*] movement = m_ForwardSpeed * transform.forward * Time.deltaTime;
      [*] }
      [*]

      [*] // Rotate the transform of the character controller by the animation's root rotation.
      [*] m_CharCtrl.transform.rotation *= m_Animator.deltaRotation;
      [*]

      [*] // Add to the movement with the calculated vertical speed.
      [*] movement += m_VerticalSpeed * Vector3.up * Time.deltaTime;
      [*]

      [*] // Move the character controller.
      [*] m_CharCtrl.Move(movement);
      [*]

      [*] // After the movement store whether or not the character controller is grounded.
      [*] m_IsGrounded = m_CharCtrl.isGrounded;
      [*]

      [*] // If Ellen is not on the ground then send the vertical speed to the animator.
      [*] // This is so the vertical speed is kept when landing so the correct landing animation is played.
      [*] if (!m_IsGrounded)
      [*] m_Animator.SetFloat(m_HashAirborneVerticalSpeed, m_VerticalSpeed);
      [*]

      [*] // Send whether or not Ellen is on the ground to the animator.
      [*] m_Animator.SetBool(m_HashGrounded, m_IsGrounded);
      [*] }
      [*]

      [*] // This is called by an animation event when Ellen swings her staff.
      [*] public void MeleeAttackStart(int throwing = 0)
      [*] {
      [*] m_InAttack = true;
      [*] }
      [*]

      [*] // This is called by an animation event when Ellen finishes swinging her staff.
      [*] public void MeleeAttackEnd()
      [*] {
      [*] m_InAttack = false;
      [*] }
      [*]

      [*]}
      [*]

      [*]