using UnityEngine; namespace ECM2.Examples.Slide { /// /// This example extends a Character (through inheritance) implementing a slide mechanic. /// public class PlayerCharacter : Character { [Space(15.0f)] public float slideImpulse = 20.0f; public float slideDownAcceleration = 20.0f; /// /// Our custom movement mode(s) id(s). /// enum ECustomMovementMode { Sliding = 1 } /// /// If sliding, return max walk speed as our speed limit. /// public override float GetMaxSpeed() { return IsSliding() ? maxWalkSpeed : base.GetMaxSpeed(); } /// /// If sliding, limit acceleration. /// public override float GetMaxAcceleration() { return IsSliding() ? maxAcceleration * 0.1f : base.GetMaxAcceleration(); } /// /// Override IsWalking (aka: grounded movement mode) to add Sliding movement mode support, /// otherwise crouch, jump will fail while sliding due its conditions checking if IsWalking. /// public override bool IsWalking() { return IsSliding() || base.IsWalking(); } /// /// Is the Character sliding? /// public bool IsSliding() { return movementMode == MovementMode.Custom && customMovementMode == (int)ECustomMovementMode.Sliding; } /// /// Determines if the character can slide in its current state. /// protected virtual bool CanSlide() { // Slide is tied to crouch, if not crouching cant slide! if (!IsGrounded()) return false; // Check allowed slide speed threshold float sqrSpeed = velocity.sqrMagnitude; float slideSpeedThreshold = maxWalkSpeedCrouched * maxWalkSpeedCrouched; return sqrSpeed >= slideSpeedThreshold * 1.02f; } /// /// Calculate sliding direction vector. /// protected virtual Vector3 CalcSlideDirection() { Vector3 slideDirection = GetMovementDirection(); if (slideDirection.isZero()) slideDirection = GetVelocity(); else if (slideDirection.isZero()) slideDirection = GetForwardVector(); slideDirection = ConstrainInputVector(slideDirection); return slideDirection.normalized; } /// /// Attempts to perform a requested slide or /// stop it if requested or cant continue sliding. /// protected virtual void CheckSlideInput() { bool isSliding = IsSliding(); bool wantsToSlide = crouchInputPressed; if (!isSliding && wantsToSlide && CanSlide()) { SetMovementMode(MovementMode.Custom, (int)ECustomMovementMode.Sliding); } else if (isSliding && (!wantsToSlide || !CanSlide())) { SetMovementMode(MovementMode.Walking); } } /// /// Handle Sliding enter / exit. /// protected override void OnMovementModeChanged(MovementMode prevMovementMode, int prevCustomMode) { // Call base method implementation base.OnMovementModeChanged(prevMovementMode, prevCustomMode); // Enter sliding movement mode... if (IsSliding()) { // Apply initial slide impulse Vector3 slideDirection = CalcSlideDirection(); characterMovement.velocity += slideDirection * slideImpulse; // Disable Character rotation SetRotationMode(RotationMode.None); } // Exit sliding movement mode... bool wasSliding = prevMovementMode == MovementMode.Custom && prevCustomMode == (int)ECustomMovementMode.Sliding; if (wasSliding) { // Re-enable Character rotation SetRotationMode(RotationMode.OrientRotationToMovement); // If falling, make sure its velocity do not exceed maxWalkSpeed if (IsFalling()) { Vector3 worldUp = -GetGravityDirection(); Vector3 verticalVelocity = Vector3.Project(velocity, worldUp); Vector3 lateralVelocity = Vector3.ClampMagnitude(velocity - verticalVelocity, maxWalkSpeed); characterMovement.velocity = lateralVelocity + verticalVelocity; } } } protected override void OnBeforeSimulationUpdate(float deltaTime) { // Call base method implementation base.OnBeforeSimulationUpdate(deltaTime); // Attempts to do a requested slide CheckSlideInput(); } /// /// Update Character's velocity while SLIDING on walkable surfaces. /// protected virtual void SlidingMovementMode(float deltaTime) { // Limit input to lateral movement only (strafing) Vector3 desiredVelocity = Vector3.Project(GetDesiredVelocity(), GetRightVector()); // Calculate new velocity characterMovement.velocity = CalcVelocity(characterMovement.velocity, desiredVelocity, groundFriction * 0.2f, false, deltaTime); // Apply slide down acceleration Vector3 slideDownDirection = Vector3.ProjectOnPlane(GetGravityDirection(), characterMovement.groundNormal).normalized; characterMovement.velocity += slideDownAcceleration * deltaTime * slideDownDirection; // Apply downwards force if (applyStandingDownwardForce) ApplyDownwardsForce(); } protected override void CustomMovementMode(float deltaTime) { // Call base method implementation base.CustomMovementMode(deltaTime); // Sliding custom movement mode if (customMovementMode == (int)ECustomMovementMode.Sliding) SlidingMovementMode(deltaTime); } } }