void HandleCharacterMovement() { // horizontal character rotation { // rotate the transform with the input speed around its local Y axis transform.Rotate( new Vector3(0f, (m_InputHandler.GetLookInputsHorizontal() * RotationSpeed * RotationMultiplier), 0f), Space.Self); } // vertical camera rotation { // add vertical inputs to the camera's vertical angle m_CameraVerticalAngle += m_InputHandler.GetLookInputsVertical() * RotationSpeed * RotationMultiplier; // limit the camera's vertical angle to min/max m_CameraVerticalAngle = Mathf.Clamp(m_CameraVerticalAngle, -89f, 89f); // apply the vertical angle as a local rotation to the camera transform along its right axis (makes it pivot up and down) PlayerCamera.transform.localEulerAngles = new Vector3(m_CameraVerticalAngle, 0, 0); } // character movement handling bool isSprinting = m_InputHandler.GetSprintInputHeld(); { if (isSprinting) { isSprinting = SetCrouchingState(false, false); } float speedModifier = isSprinting ? SprintSpeedModifier : 1f; // converts move input to a worldspace vector based on our character's transform orientation Vector3 worldspaceMoveInput = transform.TransformVector(m_InputHandler.GetMoveInput()); // handle grounded movement if (IsGrounded) { // calculate the desired velocity from inputs, max speed, and current slope Vector3 targetVelocity = worldspaceMoveInput * MaxSpeedOnGround * speedModifier; // reduce speed if crouching by crouch speed ratio if (IsCrouching) { targetVelocity *= MaxSpeedCrouchedRatio; } targetVelocity = GetDirectionReorientedOnSlope(targetVelocity.normalized, m_GroundNormal) * targetVelocity.magnitude; // smoothly interpolate between our current velocity and the target velocity based on acceleration speed CharacterVelocity = Vector3.Lerp(CharacterVelocity, targetVelocity, MovementSharpnessOnGround * Time.deltaTime); // jumping if (IsGrounded && m_InputHandler.GetJumpInputDown()) { // force the crouch state to false if (SetCrouchingState(false, false)) { // start by canceling out the vertical component of our velocity CharacterVelocity = new Vector3(CharacterVelocity.x, 0f, CharacterVelocity.z); // then, add the jumpSpeed value upwards CharacterVelocity += Vector3.up * JumpForce; // play sound AudioSource.PlayOneShot(JumpSfx); // remember last time we jumped because we need to prevent snapping to ground for a short time m_LastTimeJumped = Time.time; HasJumpedThisFrame = true; // Force grounding to false IsGrounded = false; StartCoroutine(DoubleJumpTimer()); m_GroundNormal = Vector3.up; } } // doublejump // footsteps sound float chosenFootstepSfxFrequency = (isSprinting ? FootstepSfxFrequencyWhileSprinting : FootstepSfxFrequency); if (m_FootstepDistanceCounter >= 1f / chosenFootstepSfxFrequency) { m_FootstepDistanceCounter = 0f; AudioSource.PlayOneShot(FootstepSfx); } // keep track of distance traveled for footsteps sound m_FootstepDistanceCounter += CharacterVelocity.magnitude * Time.deltaTime; } // handle air movement else { // add air acceleration CharacterVelocity += worldspaceMoveInput * AccelerationSpeedInAir * Time.deltaTime; // limit air speed to a maximum, but only horizontally float verticalVelocity = CharacterVelocity.y; Vector3 horizontalVelocity = Vector3.ProjectOnPlane(CharacterVelocity, Vector3.up); horizontalVelocity = Vector3.ClampMagnitude(horizontalVelocity, MaxSpeedInAir * speedModifier); CharacterVelocity = horizontalVelocity + (Vector3.up * verticalVelocity); // apply the gravity to the velocity CharacterVelocity += Vector3.down * GravityDownForce * Time.deltaTime; if (!IsGrounded && IsFirstJumped && m_InputHandler.GetJumpInputDown()) { // force the crouch state to false // start by canceling out the vertical component of our velocity CharacterVelocity = new Vector3(CharacterVelocity.x, 0f, CharacterVelocity.z); // then, add the jumpSpeed value upwards CharacterVelocity += Vector3.up * JumpForce; // play sound AudioSource.PlayOneShot(JumpSfx); // remember last time we jumped because we need to prevent snapping to ground for a short time m_LastTimeJumped = Time.time; HasJumpedThisFrame = true; // Force grounding to false IsFirstJumped = false; m_GroundNormal = Vector3.up; } } } // apply the final calculated velocity value as a character movement Vector3 capsuleBottomBeforeMove = GetCapsuleBottomHemisphere(); Vector3 capsuleTopBeforeMove = GetCapsuleTopHemisphere(m_Controller.height); m_Controller.Move(CharacterVelocity * Time.deltaTime); // detect obstructions to adjust velocity accordingly m_LatestImpactSpeed = Vector3.zero; if (Physics.CapsuleCast(capsuleBottomBeforeMove, capsuleTopBeforeMove, m_Controller.radius, CharacterVelocity.normalized, out RaycastHit hit, CharacterVelocity.magnitude * Time.deltaTime, -1, QueryTriggerInteraction.Ignore)) { // We remember the last impact speed because the fall damage logic might need it m_LatestImpactSpeed = CharacterVelocity; CharacterVelocity = Vector3.ProjectOnPlane(CharacterVelocity, hit.normal); } }
void Update() { // jetpack can only be used if not grounded and jump has been pressed again once in-air if (IsPlayergrounded()) { m_CanUseJetpack = false; } else if (!m_PlayerCharacterController.HasJumpedThisFrame && m_InputHandler.GetJumpInputDown()) { m_CanUseJetpack = true; } // jetpack usage bool jetpackIsInUse = m_CanUseJetpack && IsJetpackUnlocked && CurrentFillRatio > 0f && m_InputHandler.GetJumpInputHeld(); if (jetpackIsInUse) { // store the last time of use for refill delay m_LastTimeOfUse = Time.time; float totalAcceleration = JetpackAcceleration; // cancel out gravity totalAcceleration += m_PlayerCharacterController.GravityDownForce; if (m_PlayerCharacterController.CharacterVelocity.y < 0f) { // handle making the jetpack compensate for character's downward velocity with bonus acceleration totalAcceleration += ((-m_PlayerCharacterController.CharacterVelocity.y / Time.deltaTime) * JetpackDownwardVelocityCancelingFactor); } // apply the acceleration to character's velocity m_PlayerCharacterController.CharacterVelocity += Vector3.up * totalAcceleration * Time.deltaTime; // consume fuel CurrentFillRatio = CurrentFillRatio - (Time.deltaTime / ConsumeDuration); for (int i = 0; i < JetpackVfx.Length; i++) { var emissionModulesVfx = JetpackVfx[i].emission; emissionModulesVfx.enabled = true; } if (!playingJetpackSound) { jetpackEvent.start(); playingJetpackSound = true; } } else { // refill the meter over time if (IsJetpackUnlocked && Time.time - m_LastTimeOfUse >= RefillDelay) { float refillRate = 1 / (m_PlayerCharacterController.IsGrounded ? RefillDurationGrounded : RefillDurationInTheAir); CurrentFillRatio = CurrentFillRatio + Time.deltaTime * refillRate; } for (int i = 0; i < JetpackVfx.Length; i++) { var emissionModulesVfx = JetpackVfx[i].emission; emissionModulesVfx.enabled = false; } // keeps the ratio between 0 and 1 CurrentFillRatio = Mathf.Clamp01(CurrentFillRatio); if (playingJetpackSound) { jetpackEvent.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT); playingJetpackSound = false; } } }