DugganSC

Old Glide Controller

Sep 18th, 2025
210
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 18.66 KB | None | 0 0
  1. using FS_ThirdPerson;
  2. using System;
  3. using System.Collections;
  4. using Unity.VisualScripting;
  5. using UnityEngine;
  6.  
  7. namespace SD_GlidingSystem
  8. {
  9.     public class GliderController : EquippableSystemBase
  10.     {
  11.         public GliderItem currentGliderData
  12.         {
  13.             get
  14.             {
  15.                 return (itemEquipper?.EquippedItem is GliderItem gliderData)
  16.                     ? gliderData
  17.                     : null;
  18.             }
  19.         }
  20.  
  21.         public GliderObject CurrentGlider => itemEquipper?.EquippedItemObject as GliderObject;
  22.  
  23.         [Tooltip("Enables or disables the glide feature.")]
  24.         public bool enableGlide = false;
  25.        
  26.         public bool InAction { get; private set; }
  27.         public override SystemState State { get; } = SystemState.Other;
  28.         public GameObject _floatObject;
  29.  
  30.         ICharacter player;
  31.         PlayerController playerController;
  32.         LocomotionInputManager locomotionInput;
  33.         CharacterController characterController;
  34.         LocomotionController locomotionController;
  35.         ItemEquipper itemEquipper;
  36.  
  37.         [Header("Ground Check Settings")]
  38.         [Tooltip("Radius of ground detection sphere")]
  39.         [SerializeField] float groundCheckRadius = 0.7f;
  40.  
  41.         [Tooltip("Distance for ground detection ray")]
  42.         [SerializeField] float groundCheckDistance = 2f;
  43.  
  44.         [Tooltip("Offet between the player's root position and the ground detection sphere")]
  45.         [SerializeField] Vector3 groundCheckOffset = new Vector3(0f, 0.15f, 0.07f);
  46.  
  47.         [Tooltip("All layers that should be considered as ground")]
  48.         public LayerMask groundLayer = 1;
  49.  
  50.         [SerializeField] Vector3 _velocityVector;
  51.         [SerializeField] private float _parachuteDrag = 0.1f;
  52.         [SerializeField] private float _rotationSpeed = 180f;
  53.         [SerializeField] private float _fallSpeed = -0.2f;
  54.         [SerializeField] Animator _animator;
  55.         private Vector3 forwardLandForceMultiplier;
  56.  
  57.         [Header("Gliding Turning Physics")]
  58.         [SerializeField] FlightProfile flightProfile;
  59.  
  60.         [Tooltip("How much forward speed is lost to generate lift when pulling up. >1 is a net loss.")]
  61.         [SerializeField] private float _forwardMomentumCost = 1.2f;
  62.  
  63.         [Tooltip("How quickly the glider's pitch stabilizes with no input.")]
  64.         [SerializeField] private float _pitchDampening = 3f;
  65.  
  66.         [Tooltip("How quickly the glider stabilizes and levels out with no input.")]
  67.         [SerializeField] private float _rollDampening = 3f;
  68.  
  69.  
  70.         [Tooltip("How quickly the glider rolls into a turn. Higher is more responsive.")]
  71.         [SerializeField] private float _turnSpeed = 2.5f;
  72.  
  73.         [Tooltip("The maximum angle in degrees the glider can bank.")]
  74.         [SerializeField] private float _maxRollAngle = 40f;
  75.  
  76.         [Tooltip("How much extra forward speed is gained when diving.")]
  77.         [SerializeField] private float _speedBoostFromPitch = 5f;
  78.  
  79.         [Tooltip("How much lift (reduced fall speed) is generated when pitching up.")]
  80.         [SerializeField] private float _liftFromPitch = 2f;
  81.  
  82.  
  83.  
  84.         // We need a variable to track the current pitch angle
  85.         private float _currentPitch = 0f;
  86.  
  87.         // We need a variable to track the current roll angle
  88.         private float _currentRoll = 0f;
  89.  
  90.         [Header("Gliding Speed")]
  91.         [SerializeField] private float _maxGlideSpeed = 25f;
  92.  
  93.         [Header("Ragdoll Paramters")]
  94.         [Tooltip("The amount of force applied to the legs to make them sway when turning.")]
  95.         public float legSwayFactor = 50f;
  96.  
  97.         [Tooltip("The Rigidbody components of the character's legs (thighs and calves).")]
  98.         public Rigidbody[] legRigidbodies;
  99.  
  100.         private void Start()
  101.         {
  102.             player = GetComponent<ICharacter>();
  103.             playerController = GetComponent<PlayerController>();
  104.             characterController = GetComponent<CharacterController>();
  105.             locomotionController = GetComponent<LocomotionController>();
  106.             itemEquipper = GetComponent<ItemEquipper>();
  107.  
  108.             itemEquipper.OnEquip += EquipItem;
  109.             itemEquipper.OnUnEquip += UnEquipItem;
  110.  
  111.             _animator = GetComponent<Animator>();
  112.  
  113.             locomotionInput = GetComponent<LocomotionInputManager>();
  114.         }
  115.  
  116.         public void Update()
  117.         {
  118.             HandleGlideInputs();
  119.  
  120.             if (enableGlide)
  121.             {
  122.                 if (playerController.IsInAir && !InAction && GlideStartInputDown && HighEnough())
  123.                 {
  124.                     StartCoroutine(StartGliding());
  125.                 } else if (InAction && GlideStopInputDown) {
  126.                     StartCoroutine(HandleLandingMomentum());
  127.                     StartCoroutine(StopGliding());
  128.                     return;
  129.                 }
  130.  
  131.                 if (InAction && HighEnough())
  132.                 {
  133.                     HandleGlidingMovement();
  134.                 } else if (InAction && !HighEnough())
  135.                 {
  136.                     StartCoroutine(HandleLandingMomentum());
  137.                     StartCoroutine(StopGliding());
  138.                 }
  139.             }
  140.         }
  141.  
  142.         // We enter this coroutine when we decide to start the gliding process
  143.         IEnumerator HandleGliding()
  144.         {
  145.             // Check to see if we have an available glider item, even if not deployed. Exit if not.
  146.             // Equip the glider
  147.             // Yield until it is equipped
  148.  
  149.             // Play (unskippable?) deployment animation
  150.             // Yield for the time necessary for the animation to poause to let it complete.
  151.  
  152.             // While glide exit not pressed and not too close to the ground:
  153.             //  Handle gliding motion
  154.             //  Yield the thread.
  155.  
  156.             // Call StopGliding to handle winding things down
  157.  
  158.             // If on the ground, handle that animation
  159.             // Otherwise, we call handleLandingMoime
  160.  
  161.             yield return null;
  162.         }
  163.  
  164.         public override void HandleUpdate()
  165.         {
  166.             Debug.Log("In HandleUpdate");
  167.             base.HandleUpdate();
  168.  
  169.            
  170.         }
  171.  
  172.         private void HandleGlidingMovement()
  173.         {
  174.             // --- BASIC DRAG & INPUT ---
  175.             _velocityVector -= _parachuteDrag * Time.deltaTime * _velocityVector;
  176.             float v = locomotionInput.DirectionInput.y;
  177.             float h = locomotionInput.DirectionInput.x;
  178.  
  179.             // --- PITCH & ROLL CALCULATIONS ---
  180.             // Pitch
  181.             float targetPitch = v > 0 ? v * flightProfile.maxPitchAngle : v * -flightProfile.minPitchAngle;
  182.             _currentPitch = Mathf.Lerp(_currentPitch, targetPitch, flightProfile.pitchSpeed * Time.deltaTime);
  183.             if (Mathf.Approximately(v, 0f))
  184.             {
  185.                 _currentPitch = Mathf.Lerp(_currentPitch, 0f, _pitchDampening * Time.deltaTime);
  186.             }
  187.             // Roll
  188.             float targetRoll = -h * _maxRollAngle;
  189.             _currentRoll = Mathf.Lerp(_currentRoll, targetRoll, _turnSpeed * Time.deltaTime);
  190.             if (Mathf.Approximately(h, 0f))
  191.             {
  192.                 _currentRoll = Mathf.Lerp(_currentRoll, 0f, _rollDampening * Time.deltaTime);
  193.             }
  194.  
  195.             // --- VERTICAL & HORIZONTAL PHYSICS ---
  196.             Vector3 horizontalVelocity = new Vector3(_velocityVector.x, 0, _velocityVector.z);
  197.             float ySpeed = _velocityVector.y + player.Gravity * Time.deltaTime;
  198.  
  199.             // --- STATE 1: PULLING UP (Trade Speed for Lift) ---
  200.             if (_currentPitch < -0.1f)
  201.             {
  202.                 // Lift is proportional to how fast you're already going.
  203.                 float liftFactor = (_currentPitch / flightProfile.minPitchAngle); // A 0-1 value based on how far you're pulled back.
  204.                 float generatedLift = horizontalVelocity.magnitude * liftFactor * _liftFromPitch;
  205.  
  206.                 // Apply the upward lift. This can overcome gravity if you have enough speed.
  207.                 ySpeed += generatedLift * Time.deltaTime;
  208.  
  209.                 // That lift costs forward momentum (induced drag).
  210.                 float liftCost = generatedLift * _forwardMomentumCost;
  211.                 _velocityVector -= transform.forward * liftCost * Time.deltaTime;
  212.             }
  213.             // --- STATE 2: DIVING (Trade Altitude for Speed) ---
  214.             else
  215.             {
  216.                 float difference = 0;
  217.                 // If falling faster than max fall speed, convert the difference to forward thrust.
  218.                 if (ySpeed < _fallSpeed)
  219.                 {
  220.                     difference = _fallSpeed - ySpeed;
  221.                     ySpeed = _fallSpeed;
  222.                 }
  223.  
  224.                 // Add extra boost for how steeply you are diving.
  225.                 float pitchToSpeed = 0; // (_currentPitch > 0) ? (_currentPitch / flightProfile.maxPitchAngle) * _speedBoostFromPitch : 0;
  226.  
  227.                 Vector3 forwardThrust = transform.forward * (difference + pitchToSpeed);
  228.                 _velocityVector += forwardThrust;
  229.             }
  230.  
  231.             _velocityVector.y = ySpeed;
  232.  
  233.             // --- APPLY ROTATION & MOVEMENT ---
  234.             float yawChange = -_currentRoll * flightProfile.yawFromRoll * Time.deltaTime;
  235.             transform.Rotate(0, yawChange, 0, Space.World);
  236.             transform.localRotation = Quaternion.Euler(_currentPitch, transform.localEulerAngles.y, _currentRoll);
  237.  
  238.             // Clamp total speed
  239.             if (_velocityVector.magnitude > _maxGlideSpeed)
  240.             {
  241.                 _velocityVector = _velocityVector.normalized * _maxGlideSpeed;
  242.             }
  243.  
  244.             characterController.Move(_velocityVector * Time.deltaTime);
  245.         }
  246.  
  247.         private IEnumerator StartGliding()
  248.         {
  249.             Debug.Log("Start Gliding called");
  250.             if (InAction) { yield return null; }
  251.             InAction = true;
  252.  
  253.             _animator.SetBool("Gliding", true);
  254.             // Debug.Break();
  255.  
  256.             _velocityVector = characterController.velocity;
  257.  
  258.             player.OnStartSystem(this);
  259.             _animator.CrossFadeInFixedTime("Hanging Idle", 0.1f);
  260.         }
  261.  
  262.         private bool HighEnough()
  263.         {
  264.             Vector3 projectedPosition = transform.position + _velocityVector * Time.deltaTime * 5; // Project 5 frames ahead
  265.             return !CheckGround(projectedPosition);
  266.         }
  267.  
  268.         private void OnDrawGizmosSelected()
  269.         {
  270.             Gizmos.color = new Color(0, 1, 0, 0.5f);
  271.             Gizmos.DrawRay(transform.TransformPoint(groundCheckOffset), Vector3.down * groundCheckRadius);
  272.         }
  273.  
  274.         private IEnumerator LevelOutRotation()
  275.         {
  276.             float time = 0;
  277.             float duration = 0.25f; // A quarter of a second to level out
  278.  
  279.             // Capture the pitch and roll angles when we start landing
  280.             float startingRoll = _currentRoll;
  281.             float startingPitch = _currentPitch;
  282.  
  283.             while (time < duration)
  284.             {
  285.                 // Smoothly interpolate both roll and pitch from their starting angles to zero
  286.                 _currentRoll = Mathf.Lerp(startingRoll, 0f, time / duration);
  287.                 _currentPitch = Mathf.Lerp(startingPitch, 0f, time / duration);
  288.  
  289.                 // Update the character's local rotation to reflect the change
  290.                 transform.localRotation = Quaternion.Euler(_currentPitch, transform.localEulerAngles.y, _currentRoll);
  291.  
  292.                 time += Time.deltaTime;
  293.                 yield return null; // Wait for the next frame
  294.             }
  295.  
  296.             // After the loop, snap to a perfect zero rotation to ensure it's correct
  297.             _currentRoll = 0f;
  298.             _currentPitch = 0f;
  299.             transform.localRotation = Quaternion.Euler(0f, transform.localEulerAngles.y, 0f);
  300.         }
  301.  
  302.         private IEnumerator StopGliding()
  303.         {
  304.             if (!InAction) { yield return null; }
  305.             Debug.Log($"StopGliding");
  306.             InAction = false;
  307.             // _floatObject.SetActive(false);
  308.             Debug.Log("Setting Gliding to false");
  309.             _animator.SetBool("Gliding", false);
  310.             // Debug.Break();
  311.  
  312.             // Call the new coroutine to handle leveling out the character
  313.             StartCoroutine(LevelOutRotation());
  314.  
  315.             player.OnEndSystem(this);
  316.  
  317.         }
  318.  
  319.         private void OnCollisionEnter(Collision collision)
  320.         {
  321.             Debug.Log("Collision");
  322.             // If we have a collision while gliding, we stop gliding.
  323.             if (InAction)
  324.             {
  325.                 StartCoroutine(StopGliding());
  326.             }
  327.         }
  328.  
  329.         #region rootmotion
  330.  
  331.         bool prevRootMotionVal;
  332.         private string glideInputButton;
  333.         private KeyCode glideInput;
  334.  
  335.         public void EnableRootMotion()
  336.         {
  337.             prevRootMotionVal = player.UseRootMotion;
  338.             player.UseRootMotion = true;
  339.         }
  340.         public void ResetRootMotion()
  341.         {
  342.             player.UseRootMotion = prevRootMotionVal;
  343.         }
  344.  
  345.         public override void HandleOnAnimatorMove(Animator animator)
  346.         {
  347.             transform.rotation *= animator.deltaRotation;
  348.             characterController.Move(animator.deltaPosition);
  349.         }
  350.  
  351.         #endregion
  352.  
  353.         #region input manager
  354.  
  355. #if inputsystem
  356.         GlideInputActions input;
  357.  
  358.         private void OnEnable()
  359.         {
  360.             input = new GlideInputActions();
  361.             input.Enable();
  362.         }
  363.  
  364.         private void OnDisable()
  365.         {
  366.             input.Disable();
  367.         }
  368. #endif
  369.  
  370.         [Header("Input Settings")]
  371.         [Tooltip("Key to start gliding.")]
  372.         public KeyCode glideStartInput = KeyCode.Space;
  373.  
  374.         [Tooltip("Key to stop gliding.")]
  375.         public KeyCode glideStopInput = KeyCode.Space;
  376.  
  377.         [Tooltip("The button to start gliding.")]
  378.         public string glideStartButton;
  379.  
  380.         [Tooltip("The button to stop gliding.")]
  381.         public string glideStopButton;
  382.  
  383.  
  384.         public bool GlideStartInputDown { get; private set; }
  385.         public bool GlideStopInputDown { get; private set; }
  386.  
  387.         void HandleGlideInputs()
  388.         {
  389.  
  390. #if inputsystem
  391.             GlideStartInputDown = input.Glide.GlideStart.WasPerformedThisFrame();
  392.             GlideStopInputDown = input.Glide.GlideEnd.WasPerformedThisFrame();
  393. #else
  394.             GlideStartInputDown = Input.GetKeyDown(glideStartInput) || (!string.IsNullOrEmpty(glideStartButton) && Input.GetButtonDown(glideStartButton));
  395.             GlideStopInputDown = Input.GetKeyDown(glideStopInput) || (!string.IsNullOrEmpty(glideStopButton) && Input.GetButtonDown(glideStopButton));
  396. #endif
  397.         }
  398.  
  399.         #endregion
  400.  
  401.         #region Landing
  402.  
  403.         IEnumerator CrossFadeAsync(string anim, float crossFadeTime = .2f, bool enableRootmotion = false, Action onComplete = null)
  404.         {
  405.             if (enableRootmotion)
  406.                 EnableRootMotion();
  407.             _animator.CrossFadeInFixedTime(anim, crossFadeTime);
  408.             yield return null;
  409.             //while (animator.IsInTransition(0))
  410.             //{
  411.             //    yield return null;
  412.             //}
  413.             var animState = _animator.GetNextAnimatorStateInfo(0);
  414.  
  415.             float timer = 0f;
  416.  
  417.             while (timer <= animState.length)
  418.             {
  419.                 timer += Time.deltaTime * _animator.speed;
  420.                 yield return null;
  421.             }
  422.             if (enableRootmotion)
  423.                 ResetRootMotion();
  424.             onComplete?.Invoke();
  425.         }
  426.  
  427.         private IEnumerator HandleLandingMomentum()
  428.         {
  429.             Debug.Log("Calling HandleLandingMomentum");
  430.             InAction = false;
  431.             _animator.SetBool("Gliding", false);
  432.             StartCoroutine(CrossFadeAsync(AnimationNames.FallTree, .2f, false));
  433.             StartCoroutine(LevelOutRotation());
  434.  
  435.             // Calculate the velocity required for the jump landing.
  436.             float ySpeed = _velocityVector.y;
  437.  
  438.             // While the player is not grounded, apply gravity and move the player towards the landing position.
  439.             while (!locomotionController.IsGrounded)
  440.             {
  441.                 ySpeed += player.Gravity * Time.deltaTime; // Apply gravity.
  442.                 _velocityVector.y = ySpeed;
  443.                 characterController.Move(_velocityVector * Time.deltaTime);
  444.                 yield return null;
  445.             }
  446.  
  447.             // Reset velocity after landing
  448.             _velocityVector = Vector3.zero;
  449.         }
  450.  
  451.         private bool CheckGround(Vector3 position)
  452.         {
  453.             Debug.DrawRay(position + groundCheckOffset, Vector3.down, Color.red);
  454.             return Physics.SphereCast(position + groundCheckOffset, groundCheckRadius, Vector3.down, out _, groundCheckDistance, groundLayer);
  455.         }
  456.  
  457.         // Add this method to the same script to visualize the SphereCast
  458.         private void OnDrawGizmos()
  459.         {
  460.             Debug.Log("Drawing Gizmos...");
  461.             // --- Visualize the SphereCast ---
  462.             Vector3 startPoint = transform.position + groundCheckOffset;
  463.             Vector3 endPoint = startPoint + Vector3.down * groundCheckDistance;
  464.  
  465.             // Check if the cast hits anything to change the gizmo's color
  466.             bool isHit = Physics.SphereCast(startPoint, groundCheckRadius, Vector3.down, out RaycastHit hitInfo, groundCheckDistance, groundLayer);
  467.  
  468.             // Set the color based on hit status
  469.             Gizmos.color = isHit ? Color.green : Color.red;
  470.  
  471.             // Draw the starting and ending spheres of the cast
  472.             Gizmos.DrawWireSphere(startPoint, groundCheckRadius);
  473.             Gizmos.DrawWireSphere(endPoint, groundCheckRadius);
  474.  
  475.             // Draw a line connecting the centers of the two spheres
  476.             Gizmos.DrawLine(startPoint, endPoint);
  477.  
  478.             // If the cast hit something, draw a solid sphere at the exact point of contact
  479.             if (isHit)
  480.             {
  481.                 Vector3 hitCenter = startPoint + Vector3.down * hitInfo.distance;
  482.                 Gizmos.DrawSphere(hitCenter, groundCheckRadius);
  483.             }
  484.         }
  485.  
  486.  
  487.         #endregion
  488.         #region Equip And UnEquip
  489.  
  490.         public void EquipItem(EquippableItem itemdata)
  491.         {
  492.             Debug.Log("Entering EquipItem for " + itemdata.name);
  493.             if (itemdata is GliderItem)
  494.             {
  495.                 enableGlide = true;
  496.                 flightProfile = ((GliderItem)itemdata).flightProfile;
  497.             }
  498.         }
  499.         public void UnEquipItem()
  500.         {
  501.             if (CurrentGlider is GliderObject)
  502.             {
  503.                 enableGlide = false;
  504.                 flightProfile = null;
  505.             }
  506.         }
  507.  
  508.  
  509.         #endregion
  510.  
  511.  
  512.     }
  513. }
Advertisement
Add Comment
Please, Sign In to add comment