Advertisement
seansmith01

Vehicle Controller DeliveRodent

Jul 10th, 2024 (edited)
5
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 36.30 KB | Source Code | 0 0
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.InputSystem;
  5. //using Lofelt.NiceVibrations;    //Remove this when all haptics have been moved to RumbleController
  6.  
  7.  
  8. public struct HitInfo
  9. {
  10.     public bool isFrontWheel;
  11.     public Vector3 wheelHitPoint;
  12.     public Vector3 wheelHitNormal;
  13. }
  14. public class ArcadeVehicleController : MonoBehaviour
  15. {
  16.  
  17.     [SerializeField] private bool ShowDebugRays;
  18.  
  19.     Transform[] allWheelHolders = new Transform[4];
  20.     [field: SerializeField] public LayerMask DrivableSurfaceMask { get; private set; }
  21.  
  22.     private PlayerStatus playerStatus;
  23.     private DeliveryVFXHandler vFXHandler;
  24.     private CarVisualsHandler carVisualsHandler;
  25.     private RumbleController rumbleController;
  26.     [SerializeField] private bool useWheelRotationForDrag;
  27.  
  28.     [Header("Speed")]
  29.     public float DefaultMaxSpeed;
  30.     public float CurrentMaxSpeed; // Initialized to DefaultMaxSpeed on start
  31.     [SerializeField] private float MaxZVelocity; // Initialized to DefaultMaxSpeed on start
  32.     [SerializeField] private float OffRoadMaxSpeed;
  33.     [SerializeField] private float reverseMaxSpeed = 50f;
  34.     [SerializeField] private float currentAcceleration = 250f;
  35.     [SerializeField] private float defaultAcceleration;
  36.     [SerializeField] private float brakeAcceleration;
  37.     [SerializeField] private float reverseAcceleration;
  38.     [SerializeField] private float forwardTurnMultiplier;
  39.     [SerializeField] private float reverseTurnMultiplier;
  40.     [SerializeField] private float reverseDriftTurnMultiplier;
  41.     [Range(50, 500)]
  42.     [SerializeField] private float dragMultiplier = 250f;
  43.     [SerializeField] private float slipStreamMaxSpeed = 100f;
  44.  
  45.     [Header("Positioning")]
  46.     public Vector3 StartRayOffset;
  47.     [SerializeField] float centerGroundedDistance = 3f;
  48.     [SerializeField] float wheelGroundedDistance = 3f;
  49.     [SerializeField] float YOffset = 1.5f;
  50.     [SerializeField] float maxSteepness = 55f;
  51.  
  52.     [Header("DownForces")]
  53.     [SerializeField] private float downForceInAirAfterApex = 120f;
  54.     [SerializeField] private float downForceInAirBeforeApex = 100f;
  55.  
  56.     [Header("Rigidbodies")]
  57.     [SerializeField] public Rigidbody carRigidbody;
  58.  
  59.     [Header("AirControl")]
  60.     [SerializeField] private float airAcceleration = 5f;
  61.     [SerializeField] private float airMaxSidewaysSpeed = 50f;
  62.  
  63.     [Header("AnimationCurves")]
  64.     [SerializeField] private AnimationCurve turnCurve;
  65.     [SerializeField] private AnimationCurve driftInputCurve;
  66.     [Tooltip("-0.5 is going down 45 degrees, 0.5 is going up 45 degrees")]
  67.     [SerializeField] private AnimationCurve slopeMultiplierCurve;
  68.     [SerializeField] private AnimationCurve driftTorqueMultiplierCurve;
  69.     [SerializeField] private AnimationCurve driftSidewaysMultiplierCurve;
  70.  
  71.     [Header("J_Turn")]
  72.     public float jTurnDriftThreshold = 0.5f;
  73.     public float spinForce = 100f;
  74.     [SerializeField] private float jturnspeed;
  75.  
  76.     [Header("Drifting")]
  77.     [SerializeField] private float driftTurnTorque = 25f;
  78.     [SerializeField] private float driftSidewaysForce = 350f;
  79.     [SerializeField] private float[] driftBoostThresholds;
  80.     [SerializeField] private float[] driftBoostSpeeds;
  81.     [SerializeField] private float[] driftBoostAccelerations;
  82.     [SerializeField] private float[] driftBoostDurations;
  83.  
  84.     [Header("SpeedBoosts")]
  85.     [SerializeField] private float boostPanelSpeedIncrease;
  86.     [SerializeField] private float boostPanelAccelerationIncrease;
  87.     [SerializeField] private float boostDefualtAccelerationIncrease;
  88.     [SerializeField] private float boostPanelDuration;
  89.     [SerializeField] private float powerUpBoostSpeedIncrease;
  90.     [SerializeField] private float powerUpBoostAccelerationIncrease;
  91.     [SerializeField] private float powerUpBoostDuration;
  92.     private float initialPowerUpBoostTime;
  93.  
  94.     bool bumpedWhilstDrifting;
  95.     private float driftTimer;
  96.     private float steeringSign;
  97.  
  98.     List<Vector3> groundhits = new List<Vector3>();
  99.     public bool[] WheelIsGrounded = new bool[4];
  100.     public RaycastHit[] WheelRayHits = new RaycastHit[4];
  101.     Vector3[] wheelHitPositions = new Vector3[4];
  102.  
  103.     bool canHorn = true;
  104.     [SerializeField] float hornDelay = 1.5f;
  105.  
  106.     [HideInInspector]
  107.     public Vector3 carRelativeVelocity;
  108.  
  109.     [Header("SpeedBoost")]
  110.     private bool isBoosting;
  111.  
  112.     CarAudioHandler carAudioHandler;
  113.     [SerializeField] Transform carBodyHolder;
  114.  
  115.     public float CurrentSpeed { get; private set; }
  116.     public float SteerInput { get; private set; }
  117.     public float AccelerationInput { get; private set; }
  118.     public float BrakeInput { get; private set; }
  119.     public bool HandBrakeInput { get; private set; }
  120.     public bool LockedIntoDrift { get; private set; }
  121.     public float DriftSteerInput { get; private set; }
  122.     public bool IsGrounded { get; private set; }
  123.     public int CurrentBoostLevel; // Indicates current boost level, if any
  124.     public bool IsEmmittingTrail { get; set; }
  125.     public bool IsStunned { get; private set; }
  126.     public int PlayerID { get; private set; }
  127.  
  128.     public bool isSlipstreamed;
  129.     public float slipStreamTimer;
  130.     private void Start()
  131.     {
  132.         // Initialize references to various components
  133.         vFXHandler = GetComponentInChildren<DeliveryVFXHandler>();
  134.         carAudioHandler = GetComponentInChildren<CarAudioHandler>();
  135.         carVisualsHandler = GetComponentInChildren<CarVisualsHandler>();
  136.         rumbleController = GetComponent<RumbleController>();
  137.  
  138.         // Assign specific references needed for functionality
  139.         carBodyHolder = carVisualsHandler.transform;
  140.         allWheelHolders = carVisualsHandler.AllWheelHolders;
  141.         playerStatus = GetComponent<PlayerStatus>();
  142.     }
  143.  
  144.     public void OnSteerInput(InputAction.CallbackContext context)
  145.     {
  146.         // Read and assign steer input if controls are enabled
  147.         if (InputManager.instance.controlsEnabled)
  148.         {
  149.             SteerInput = context.ReadValue<float>();
  150.         }
  151.     }
  152.  
  153.     public void OnAccelerateButton(InputAction.CallbackContext context)
  154.     {
  155.         // Read and assign accelerate input if controls are enabled; otherwise set to 0
  156.         AccelerationInput = InputManager.instance.controlsEnabled ? context.ReadValue<float>() : 0;
  157.     }
  158.  
  159.     public void OnBrakeInput(InputAction.CallbackContext context)
  160.     {
  161.         // Read and assign brake input if controls are enabled; otherwise set to 0
  162.         BrakeInput = InputManager.instance.controlsEnabled ? context.ReadValue<float>() : 0;
  163.     }
  164.  
  165.     public void OnHandBrakeButton(InputAction.CallbackContext context)
  166.     {
  167.         // Activate handbrake input and trigger visuals if controls are enabled
  168.         if (InputManager.instance.controlsEnabled)
  169.         {
  170.             HandBrakeInput = context.action.WasPressedThisFrame();
  171.             carVisualsHandler.BodyScaleSpring(new Vector3(3f, 0.8f, 3f));
  172.         }
  173.     }
  174.  
  175.     public void OnHornButton(InputAction.CallbackContext context)
  176.     {
  177.         // Trigger horn sound and delay functionality if controls are enabled and horn can be used
  178.         if (context.action.WasPressedThisFrame() && canHorn)
  179.         {
  180.             canHorn = false;
  181.             carAudioHandler.PlayHornSound();
  182.             StartCoroutine(HornDelay());
  183.         }
  184.     }
  185.  
  186.     RaycastHit centreHit;
  187.  
  188.     float driftTorqueMulitplier = 0.5f;
  189.     float driftSidewaysForceMultiplier = 1.5f; // Start at 1.5 of itself
  190.     private void Update()
  191.     {
  192.         timeSinceLastCollision += Time.deltaTime;
  193.        
  194.         CurrentSpeed = carRelativeVelocity.z;
  195.         DriftInputHandling();
  196.     }
  197.  
  198.     void FixedUpdate()
  199.     {
  200.         // Perform ground checks
  201.         GroundCheck();
  202.         GroundCheckWheels();
  203.  
  204.         // Rotate car to align with surface normal
  205.         RotateToSurface();
  206.  
  207.         // Calculate relative velocity in local space
  208.         carRelativeVelocity = carRigidbody.transform.InverseTransformDirection(carRigidbody.velocity);
  209.  
  210.         // Handle grounded state
  211.         if (IsGrounded)
  212.         {
  213.             // Reset y velocity if grounded
  214.             carRigidbody.velocity = transform.TransformDirection(new Vector3(carRelativeVelocity.x, 0, carRelativeVelocity.z));
  215.         }
  216.         else
  217.         {
  218.             // Adjust physics settings for air control
  219.             carRigidbody.drag = 0.2f;
  220.             carRigidbody.MoveRotation(Quaternion.Slerp(carRigidbody.rotation, Quaternion.FromToRotation(carRigidbody.transform.up, Vector3.up) * carRigidbody.transform.rotation, 0.05f));
  221.             carRigidbody.velocity = Vector3.Lerp(carRigidbody.velocity, transform.forward * carRelativeVelocity.z, airAcceleration * Time.deltaTime);
  222.  
  223.             // Apply downward force based on current y velocity
  224.             carRigidbody.AddForce(Vector3.down * (carRigidbody.velocity.y < 5 ? downForceInAirAfterApex : downForceInAirBeforeApex));
  225.         }
  226.  
  227.         // Return if stunned
  228.         if (IsStunned)
  229.         {
  230.             return;
  231.         }
  232.  
  233.         // Air control when not grounded and return
  234.         if (!IsGrounded)
  235.         {
  236.             AirControl();
  237.             return;
  238.         }
  239.  
  240.         // Reset drag when grounded
  241.         carRigidbody.drag = 1f;
  242.  
  243.         // Handle Slipstream effect
  244.         if (isSlipstreamed)
  245.         {
  246.             vFXHandler.PlaySpeedLines();
  247.             CurrentMaxSpeed = slipStreamMaxSpeed;
  248.             slipStreamTimer -= Time.deltaTime;
  249.             if (slipStreamTimer <= 0)
  250.             {
  251.                 vFXHandler.StopSpeedLines();
  252.                 CurrentMaxSpeed = DefaultMaxSpeed;
  253.                 isSlipstreamed = false;
  254.             }
  255.         }
  256.  
  257.         // Ensure CurrentMaxSpeed does not drop below DefaultMaxSpeed
  258.         if (CurrentMaxSpeed < DefaultMaxSpeed)
  259.         {
  260.             CurrentMaxSpeed = DefaultMaxSpeed;
  261.         }
  262.  
  263.         // Reset velocity if below minimum threshold
  264.         if (carRigidbody.velocity.magnitude < 2.5f)
  265.         {
  266.             carRigidbody.velocity = Vector3.zero;
  267.         }
  268.  
  269.         // Apply driving logic and limit maximum forward speed
  270.         DrivingLogic();
  271.         LimitMaxForwardSpeed();
  272.     }
  273.  
  274.     private void DrivingLogic()
  275.     {
  276.         UpdateMaxSpeed();
  277.         TurningLogic();
  278.         ForwardBackwardLogic();
  279.     }
  280.     private void ForwardBackwardLogic()
  281.     {
  282.         // print(carRelativeVelocity.z);
  283.        
  284.  
  285.         if (Mathf.Abs(AccelerationInput) > 0.1f || isBoosting)
  286.         {
  287.             // If is boosting from drift, set speed strait to 1, else allow the player to feather the joystick to get and accel input between 0 and 1
  288.             float _speed = (isBoosting) ? 1 : AccelerationInput;
  289.             // Multiply speed by current max speed
  290.             _speed *= CurrentMaxSpeed;
  291.             // Multiply speed depending on the steepness of the slope the car is on
  292.             _speed *= SlopeMultiplier();
  293.  
  294.             // Calculate the time it takes to reach the target speed
  295.             float timeToMaxSpeed = _speed / currentAcceleration;
  296.             // Calculate the fraction of time that has passed towards reaching the target speed
  297.             // Clamp so t is always between 0 and 1.
  298.             float t = Mathf.Clamp01(Time.deltaTime / timeToMaxSpeed);
  299.  
  300.             // Interpolate the current velocity towards the target velocity using Lerp with t as time between
  301.             carRigidbody.velocity = Vector3.Lerp(carRigidbody.velocity, transform.forward * _speed, t);
  302.         }
  303.         //reverse logic
  304.         if (Mathf.Abs(BrakeInput) > 0.1f)
  305.         {
  306.             if (!LockedIntoDrift)
  307.             {
  308.  
  309.                 float _speed = BrakeInput;
  310.                 // Multiply speed by reverse max speed
  311.                 _speed *= reverseMaxSpeed;
  312.  
  313.                 float timeToMaxSpeed = carRelativeVelocity.z > 0 ? reverseMaxSpeed / brakeAcceleration : reverseMaxSpeed / reverseAcceleration;
  314.                 float t = Mathf.Clamp01(Time.deltaTime / timeToMaxSpeed);
  315.                 carRigidbody.velocity = Vector3.Lerp(carRigidbody.velocity, -transform.forward * _speed, t);
  316.             }
  317.  
  318.         }
  319.  
  320.         //friction simulation (velocity dampening)
  321.         Vector3 carVelocityXZ = new Vector3(carRigidbody.velocity.x, 0, carRigidbody.velocity.z).normalized;
  322.         Vector3 carRightXZ = new Vector3(transform.right.x, 0, transform.right.z).normalized;
  323.         float dot = Vector3.Dot(carVelocityXZ, carRightXZ);
  324.         carRigidbody.AddForce(transform.right * -dot * dragMultiplier);
  325.     }    
  326.     private void TurningLogic()
  327.     {
  328.         float _turnCurveMultiplier = turnCurve.Evaluate(Mathf.Abs(carRelativeVelocity.z / CurrentMaxSpeed)); // divide by CurrentMaxSpeed so it goes between 0 and 1
  329.         //_turnCurveMultiplier *= Mathf.Abs(carRelativeVelocity.z);
  330.         _turnCurveMultiplier *= 100f;
  331.         //if driving forwards
  332.         if (carRelativeVelocity.z > 2.5f)
  333.         {
  334.             //if drifting
  335.             if (LockedIntoDrift)
  336.             {
  337.                 // Add torque based on the DriftSteerInput and the driftTurnMultiplier
  338.                 carRigidbody.AddTorque(Vector3.up * DriftSteerInput * driftTurnTorque * driftTorqueMulitplier * _turnCurveMultiplier);
  339.                 // Add sideways force against the player to make them slide out whilst drifting
  340.                 carRigidbody.AddForce(transform.right * -steeringSign * driftSidewaysForce * driftSidewaysForceMultiplier * carRelativeVelocity.z, ForceMode.Acceleration);
  341.                
  342.             }
  343.             else
  344.             {
  345.                 carRigidbody.AddTorque(Vector3.up * SteerInput * forwardTurnMultiplier * _turnCurveMultiplier);
  346.             }
  347.         }
  348.         //driving backwards
  349.         else if(carRelativeVelocity.z < -2.5f)
  350.         {
  351.             // Check if the player initiated the J-turn
  352.             if (LockedIntoDrift)
  353.             {
  354.                 // Reverse the car's movement
  355.                 float _speed = BrakeInput * reverseMaxSpeed;
  356.                 // Multiply speed by reverse max speed
  357.                 float timeToMaxSpeed = reverseMaxSpeed / reverseAcceleration;
  358.                 float t = Mathf.Clamp01(Time.deltaTime / timeToMaxSpeed);
  359.  
  360.                 // Apply steering input
  361.                 carRigidbody.AddTorque(Vector3.up * -1 * SteerInput * reverseTurnMultiplier * _turnCurveMultiplier);
  362.  
  363.                 // Check if the car is drifting
  364.                 if (carRigidbody.angularVelocity.magnitude > jTurnDriftThreshold)
  365.                 {
  366.                     //isDriftingReverse = true;
  367.  
  368.                     _speed = BrakeInput * jturnspeed;
  369.                     carRigidbody.AddTorque(Vector3.up * -1 * spinForce * 100f * SteerInput);
  370.                 }
  371.                 carRigidbody.velocity = Vector3.Lerp(carRigidbody.velocity, -transform.forward * _speed, t);
  372.  
  373.             }
  374.             else
  375.             {
  376.                 carRigidbody.AddTorque(Vector3.up * -1 * SteerInput * reverseTurnMultiplier * _turnCurveMultiplier);
  377.             }            
  378.         }
  379.     }    
  380.     private void DriftInputHandling()
  381.     {
  382.         //If holding drift input and turning and hasn't been bumped whilst drifting
  383.         if (HandBrakeInput && Mathf.Abs(SteerInput) > 0.1f && !bumpedWhilstDrifting && IsGrounded)
  384.         {
  385.             //If not locked into drift, locked into drift is true so this is called once
  386.             if (!LockedIntoDrift)
  387.             {
  388.                 LockedIntoDrift = true;
  389.                 //steering directional sign is locked and can't be changed until the player starts a new drift
  390.                 steeringSign = Mathf.Sign(SteerInput);
  391.  
  392.                 rumbleController.PlayRumble(0.5f, 1f, 0.5f);
  393.             }
  394.         }
  395.         if (LockedIntoDrift)
  396.         {
  397.             driftSidewaysForceMultiplier = driftSidewaysMultiplierCurve.Evaluate(driftTimer);
  398.             driftTorqueMulitplier = driftTorqueMultiplierCurve.Evaluate(driftTimer);
  399.  
  400.             //increase drift timer (used for speed boosts) if grounded
  401.             if (IsGrounded && (AccelerationInput > 0.1f || BrakeInput > 0.1f))
  402.             {
  403.                 //Increase drift timer
  404.                 IncreaseDriftTimer();
  405.             }
  406.  
  407.             //evaluate the drift curve
  408.             //-1 steer input corresponds to 0.5 on the curve so if the player is drifting right but holding left, the driftSteeringInput will be 0.5 not -1
  409.             //1 steer input corresponds to 1.5, on the curve so if the player is drifting right and holding right, the driftSteeringInput will be 1.5 not 1
  410.             //evaluating the curve with the steer input multiplied by the sign will invert these results
  411.             DriftSteerInput = driftInputCurve.Evaluate(SteerInput * steeringSign);
  412.             //multiplying the results by the steering sign will make the car drift
  413.             DriftSteerInput *= steeringSign;
  414.         }
  415.         //If player is not holding drift button
  416.         if (!HandBrakeInput)
  417.         {
  418.             if (LockedIntoDrift)
  419.             {
  420.                 DriftComplete();
  421.             }
  422.             driftTimer = 0f;
  423.             steeringSign = 0f;
  424.             DriftSteerInput = 0f;
  425.             LockedIntoDrift = false;
  426.             bumpedWhilstDrifting = false;
  427.         }
  428.     }
  429.     private void GroundCheck()
  430.     {
  431.         //the start of the ray will be above the car to prevent going through the floor, since were apllying a down force
  432.         //ray shoots worldspace down, if it hits, the cars position will be offset in world space up
  433.         Vector3 rayStartPosition = transform.position + StartRayOffset;
  434.         Vector3 rayDirection = -Vector3.up;
  435.         float rayDistance = centerGroundedDistance + StartRayOffset.y;
  436.  
  437.         Debug.DrawRay(rayStartPosition, rayDirection * rayDistance, Color.red);
  438.  
  439.         if (Physics.Raycast(rayStartPosition, rayDirection, out centreHit, rayDistance, DrivableSurfaceMask))
  440.         {
  441.             if (Vector3.Angle(centreHit.normal, Vector3.up) < maxSteepness)
  442.             {
  443.                 // Get the layer of the hit object
  444.                 //IsOffRoading = centreHit.collider.CompareTag("OffRoad");
  445.                 IsGrounded = true;
  446.                 return;
  447.             }
  448.         }
  449.         IsGrounded = false;
  450.     }
  451.     void GroundCheckWheels()
  452.     {
  453.         //each wheel cast a ray locally down from it's centre (or higher)
  454.         //if the ray hits a surface the wheel will be driving on that point
  455.         //an invisible plane will be made with all the points
  456.         //the car will be rotated to the normal of that plane (if 4 wheels are grounded?)
  457.         groundhits.Clear();        
  458.         for (int i = 0; i < 4; i++)
  459.         {
  460.             Vector3 startPos = allWheelHolders[i].position + transform.up * StartRayOffset.y;
  461.             Vector3 rayDirection = -transform.up; // shoot ray down from car locally
  462.             float rayDistance = wheelGroundedDistance + StartRayOffset.y;
  463.             //Debug.DrawRay(startPos, rayDirection * rayDistance, Color.blue);
  464.             if (Physics.Raycast(startPos, rayDirection, out WheelRayHits[i], rayDistance, DrivableSurfaceMask))
  465.             {
  466.                 if (Vector3.Angle(WheelRayHits[i].normal, Vector3.up) < maxSteepness)
  467.                 {
  468.                     groundhits.Add(WheelRayHits[i].point);
  469.                     WheelIsGrounded[i] = true;
  470.  
  471.                 }
  472.                 else
  473.                 {
  474.                     WheelIsGrounded[i] = false;
  475.                 }
  476.             }
  477.             else
  478.             {
  479.                 WheelIsGrounded[i] = false;
  480.             }
  481.         }
  482.     }
  483.     Vector3 normalFromPlaneUnderWheels;
  484.     // Adjusts the car's orientation to match the surface beneath the wheels
  485.     private void RotateToSurface()
  486.     {
  487.         // Store hit points below each grounded wheel or local point down from the wheel if not grounded
  488.         for (int i = 0; i < 4; i++)
  489.         {
  490.             if (WheelIsGrounded[i])
  491.             {
  492.                 wheelHitPositions[i] = WheelRayHits[i].point;  // Store hit point below the grounded wheel
  493.             }
  494.             else
  495.             {
  496.                 wheelHitPositions[i] = allWheelHolders[i].position + (transform.up * -wheelGroundedDistance);  // Use local point down from the wheel if not grounded
  497.             }
  498.         }
  499.  
  500.         // Create a plane using points locally down from the wheels
  501.         Vector3 a = wheelHitPositions[0];
  502.         Vector3 b = wheelHitPositions[1];
  503.         Vector3 c = wheelHitPositions[2];
  504.         Vector3 d = wheelHitPositions[3];
  505.         Vector3 x = c - a + d - b;
  506.         Vector3 y = c - d + a - b;
  507.  
  508.         // Calculate the normal vector from the plane
  509.         normalFromPlaneUnderWheels = Vector3.Cross(x, y);
  510.  
  511.         // Rotate the car to match the calculated surface normal
  512.         carRigidbody.MoveRotation(Quaternion.Slerp(transform.rotation, Quaternion.FromToRotation(transform.up, normalFromPlaneUnderWheels) * transform.rotation, rotateToSurfaceSpeed));
  513.  
  514.         // Set the car's position to the ray cast hit below + an offset if grounded
  515.         if (IsGrounded)
  516.         {
  517.             transform.position = new Vector3(transform.position.x, centreHit.point.y + YOffset, transform.position.z);
  518.         }
  519.     }
  520.     float rotateToSurfaceSpeed = 0.2f; // used to be 0.8 but 0.2 is ssooooooo much smoother
  521.     private void AirControl()
  522.     {
  523.         // Reset the car's rotation from it's local up vector to the world's up vector
  524.         // Soesn't need to slerp by a time delta time value because it is in fixed update
  525.  
  526.         float sidewayVelocity = carRelativeVelocity.x + SteerInput * airAcceleration;
  527.         carRelativeVelocity.x = Mathf.Clamp(sidewayVelocity, -airMaxSidewaysSpeed, airMaxSidewaysSpeed);
  528.         carRigidbody.velocity = transform.TransformDirection(carRelativeVelocity);
  529.     }
  530.     private float SlopeMultiplier()
  531.     {
  532.         // Calculate the y-component product of the transform's up and forward vectors
  533.         // Backforce depending on slopes (cars transform up is rotated to) and which way the car is facing
  534.         // If the car is on a 45 degree slope and driving up, this number will be 0.5
  535.         // If it's driving down it will be -0.5
  536.         // If its driving parallel, it'll be 0
  537.         float _yProduct = transform.up.y * transform.forward.y;
  538.         return slopeMultiplierCurve.Evaluate(_yProduct);
  539.     }
  540.     private void IncreaseDriftTimer()
  541.     {
  542.         driftTimer += Time.deltaTime;
  543.  
  544.         // Iterate through the timesForDifferentDriftBoosts array to find the maximum BoostLevel
  545.         for (int i = 0; i < driftBoostThresholds.Length; i++)
  546.         {
  547.             // If the driftTimer is greater than the current boost threshold's time, update BoostLevel
  548.             if (driftTimer > driftBoostThresholds[i])
  549.             {
  550.                 CurrentBoostLevel = i + 1; // BoostLevel is one more than the current index, since arrays are zero-indexed
  551.                 //GamepadRumbler.SetCurrentGamepad(PlayerID);
  552.                 //HapticPatterns.PlayConstant(CurrentBoostLevel * 0.1f, 0.75f, 1.3f);
  553.                 rumbleController.PlayRumble((CurrentBoostLevel + 1) * 0.1f, (-1 + 2 * CurrentBoostLevel), 1.3f); //Change this so one shot is played every certain number of frames instead
  554.             }
  555.         }
  556.     }
  557.     private void DriftComplete()
  558.     {
  559.         if (CurrentBoostLevel > 0)
  560.         {
  561.             float speed_increase = driftBoostSpeeds[CurrentBoostLevel - 1];
  562.             float accel_increase = boostDefualtAccelerationIncrease;
  563.             float duration = driftBoostDurations[CurrentBoostLevel - 1];
  564.             driftType = CurrentBoostLevel-1;
  565.             initialDriftTime = Time.time;
  566.         }
  567.         // Reset values
  568.         driftTimer = 0;
  569.         CurrentBoostLevel = 0;
  570.         /*
  571.         HapticController.Loop(false);
  572.         HapticController.clipLevel = 0.25f;
  573.         HapticController.clipFrequencyShift = 0.0f;
  574.         HapticController.Stop();
  575.         */
  576.     }
  577.     private void DriftCancel()
  578.     {
  579.         //reset values
  580.         bumpedWhilstDrifting = true;
  581.         LockedIntoDrift = false;
  582.         driftTimer = 0;
  583.         CurrentBoostLevel = 0;
  584.     }
  585.     public void BoostPanelEnter()
  586.     {
  587.         initialBoostPanelTime = Time.time;  
  588.     }
  589.    
  590.  
  591.     // Method to update the current and maximum speed of the car based on various conditions
  592.     private void UpdateMaxSpeed()
  593.     {
  594.         // Reset to default values
  595.         currentAcceleration = defaultAcceleration;
  596.         CurrentMaxSpeed = DefaultMaxSpeed;
  597.  
  598.         // Check for slipstream effect
  599.         if (isSlipstreamed)
  600.         {
  601.             vFXHandler.PlaySpeedLines();  // Play speed lines visual effect
  602.             CurrentMaxSpeed = slipStreamMaxSpeed;  // Set maximum speed to slipstream maximum
  603.             slipStreamTimer -= Time.deltaTime;
  604.  
  605.             // Disable slipstream effect if timer expires
  606.             if (slipStreamTimer <= 0)
  607.             {
  608.                 if (!isBoosting)  // Check if no other boost is active
  609.                 {
  610.                     vFXHandler.StopSpeedLines();  // Stop speed lines visual effect
  611.                 }
  612.                 CurrentMaxSpeed = DefaultMaxSpeed;  // Reset maximum speed to default
  613.                 isSlipstreamed = false;  // Disable slipstream flag
  614.             }
  615.         }
  616.  
  617.         bool checkBoosting = false;  // Flag to check if any boost is active
  618.  
  619.         // Check for boosts from boost panels
  620.         if (initialBoostPanelTime + boostPanelDuration > Time.time)
  621.         {
  622.             currentAcceleration += boostPanelAccelerationIncrease;
  623.             CurrentMaxSpeed += boostPanelSpeedIncrease;
  624.             checkBoosting = true;
  625.         }
  626.  
  627.         // Check for boosts from power-up items
  628.         if (initialPowerUpBoostTime + powerUpBoostDuration > Time.time)
  629.         {
  630.             currentAcceleration += powerUpBoostAccelerationIncrease;
  631.             CurrentMaxSpeed += powerUpBoostSpeedIncrease;
  632.             checkBoosting = true;
  633.         }
  634.  
  635.         // Check for boosts from drifting (based on drift type)
  636.         if (driftType >= 0 && driftType <= 3)
  637.         {
  638.             if (initialDriftTime + driftBoostDurations[driftType] > Time.time)
  639.             {
  640.                 currentAcceleration += driftBoostAccelerations[driftType];
  641.                 CurrentMaxSpeed += driftBoostSpeeds[driftType];
  642.                 checkBoosting = true;
  643.             }
  644.         }
  645.  
  646.         // Disable speed boost visuals if no boost is active
  647.         if (!checkBoosting && isBoosting)
  648.         {
  649.             SpeedBoostVisualDisable();
  650.         }
  651.         // Enable speed boost visuals if any boost is active
  652.         else if (checkBoosting && !isBoosting)
  653.         {
  654.             SpeedBoostVisualEnable();
  655.         }
  656.     }
  657.     // Method to initialize the power-up boost, recording the start time
  658.     public void BoostPowerUpFunction()
  659.     {
  660.         initialPowerUpBoostTime = Time.time;
  661.     }
  662.  
  663.     // Coroutine to handle the speed boost power-up, increasing speed and acceleration for a duration
  664.     IEnumerator SpeedBoost(float speedIncreaseAmount, float accelIncreaseAmount, float boostDuration)
  665.     {
  666.         currentBoosts++;  // Increment the current number of boosts
  667.         CurrentMaxSpeed += speedIncreaseAmount;  // Increase maximum speed
  668.         currentAcceleration += accelIncreaseAmount;  // Increase acceleration
  669.         SpeedBoostVisualEnable();  // Enable speed boost visuals
  670.  
  671.         yield return new WaitForSeconds(boostDuration);  // Wait for the boost duration
  672.  
  673.         currentBoosts--;  // Decrement the current number of boosts
  674.         currentAcceleration -= accelIncreaseAmount;  // Revert acceleration increase
  675.         CurrentMaxSpeed -= speedIncreaseAmount;  // Revert speed increase
  676.  
  677.         // If no more active boosts, disable boost visuals
  678.         if (currentBoosts == 0)
  679.         {
  680.             SpeedBoostVisualDisable();
  681.         }
  682.     }
  683.  
  684.     // Method to enable visual effects for speed boost
  685.     private void SpeedBoostVisualEnable()
  686.     {
  687.         isBoosting = true;
  688.         GetComponentInChildren<CameraManager>().IncreaseFieldOfView();  // Increase camera field of view
  689.         vFXHandler.PlaySpeedLines();  // Play speed lines effect
  690.         vFXHandler.PlayBoostExhaustFlame();  // Play boost exhaust flame effect
  691.         carVisualsHandler.BodyScaleSpring(new Vector3(5.0f, 0.5f, 5.0f));  // Apply visual scaling effect to car body
  692.     }
  693.  
  694.     // Method to disable visual effects for speed boost
  695.     private void SpeedBoostVisualDisable()
  696.     {
  697.         isBoosting = false;
  698.         vFXHandler.StopSpeedLines();  // Stop speed lines effect
  699.         vFXHandler.StopBoostExhaustFlame();  // Stop boost exhaust flame effect
  700.     }
  701.  
  702.     // Public method to initiate the stunned action coroutine
  703.     public void StunnedActionFunction(bool spin, Vector3 stunDirection, float duration)
  704.     {
  705.         StartCoroutine(StunnedAction(spin, stunDirection, duration));
  706.     }
  707.  
  708.     // Coroutine to handle the stunned action, including spinning and duration
  709.     IEnumerator StunnedAction(bool spin, Vector3 stunDirection, float duration)
  710.     {
  711.         if (!playerStatus.Invincible)
  712.         {
  713.             // Reduce the car's velocity by half
  714.             carRigidbody.velocity = carRigidbody.velocity / 2;
  715.  
  716.             IsStunned = true;
  717.  
  718.             // Play rumble effect to indicate the stun
  719.             rumbleController.PlayOneShot(0.5f, 0.5f);
  720.  
  721.             // If spin is true, apply stun force and rotate the car
  722.             if (spin)
  723.             {
  724.                 carRigidbody.AddForce(stunDirection * stunForce, ForceMode.Impulse);
  725.                 float numberOfRotations = 3;
  726.                 float elapsedTime = 0.0f;
  727.                 carVisualsHandler.BodyScaleSpring(new Vector3(8, 0.1f, 8));
  728.                 vFXHandler.PlayStunEffect();
  729.  
  730.                 // Rotate the car over the duration of the stun
  731.                 while (elapsedTime < duration)
  732.                 {
  733.                     elapsedTime += Time.deltaTime;
  734.                     float angle = Mathf.Lerp(0.0f, 360.0f * numberOfRotations, elapsedTime / duration);
  735.                     carBodyHolder.localRotation = Quaternion.Euler(transform.GetChild(0).rotation.x, angle, transform.GetChild(0).rotation.z);
  736.                     yield return null;
  737.                 }
  738.                 carBodyHolder.localRotation = Quaternion.Euler(transform.GetChild(0).rotation.x, 0, transform.GetChild(0).rotation.z);
  739.             }
  740.             // If not spinning, simply wait for the duration
  741.             else
  742.             {
  743.                 yield return new WaitForSeconds(duration);
  744.             }
  745.  
  746.             IsStunned = false;
  747.         }
  748.     }
  749.  
  750.     // Public method to initiate the slow to stop coroutine
  751.     public void SlowToStopFunction()
  752.     {
  753.         StartCoroutine(SlowToStop());
  754.     }
  755.  
  756.     // Coroutine to gradually slow the car to a complete stop
  757.     IEnumerator SlowToStop()
  758.     {
  759.         // Disable speed boost visuals and cancel drift
  760.         SpeedBoostVisualDisable();
  761.         DriftCancel();
  762.  
  763.         playerStatus.IsStopping = true;
  764.         Vector3 startVelocity = carRigidbody.velocity;
  765.         Vector3 targetVelocity = carRigidbody.velocity * 0f;
  766.         float elapsedTime = 0.0f;
  767.         float slowDownDuration = 1.5f;
  768.  
  769.         // Gradually reduce the car's velocity to zero
  770.         while (elapsedTime < slowDownDuration)
  771.         {
  772.             elapsedTime += Time.deltaTime;
  773.             carRigidbody.velocity = Vector3.Lerp(startVelocity, targetVelocity, elapsedTime / slowDownDuration);
  774.             yield return null;
  775.         }
  776.  
  777.         // Ensure the car's velocity is exactly zero
  778.         carRigidbody.velocity = Vector3.zero;
  779.         playerStatus.IsStopping = false;
  780.     }
  781.  
  782.     // Method to handle behavior when the car stays in a trigger
  783.     private void OnTriggerStay(Collider other)
  784.     {
  785.         // Check if the car is in the slipstream of another vehicle
  786.         if (other.CompareTag("SlipStreamTrigger") && other.transform.root.GetComponent<ArcadeVehicleController>().IsEmmittingTrail == true)
  787.         {
  788.             isSlipstreamed = true;
  789.             slipStreamTimer = 1f;
  790.         }
  791.     }
  792.  
  793.     // Method to limit the car's maximum forward speed
  794.     void LimitMaxForwardSpeed()
  795.     {
  796.         if (carRelativeVelocity.z > MaxZVelocity)
  797.         {
  798.             carRigidbody.velocity = transform.TransformDirection(new Vector3(carRelativeVelocity.x, carRelativeVelocity.y, MaxZVelocity));
  799.         }
  800.     }
  801.     float initialBoostPanelTime;
  802.     int driftType = 0;
  803.     float initialDriftTime = 0;
  804.     int currentBoosts = 0;
  805.     [SerializeField] float stunForce = 50f;
  806.     [SerializeField] float UTurnDuration = 0.8f;
  807.     [SerializeField] float UTurnTargetSpeed = 15f;
  808.     [SerializeField] float backForceFromSpringyObstacle = 75f;
  809.     [SerializeField] float timeSinceLastCollision = 0f;
  810.     [SerializeField] float minCarSpeed = 20f;
  811.  
  812.     private void OnCollisionEnter(Collision collision)
  813.     {
  814.         Transform collisionTransform = collision.transform;
  815.  
  816.         // Play the appropriate collision audio based on the collision object
  817.         PlayCollisionAudio(collisionTransform);
  818.  
  819.         // Determine if drift and boost should be cancelled based on collision
  820.         bool shouldCancelDrift = false;
  821.         bool shouldCancelBoost = false;
  822.  
  823.         // Check if the collision object is an obstacle and if it's a springy obstacle
  824.         if (collisionTransform.TryGetComponent<ObstacleManager>(out ObstacleManager obstacle))
  825.         {
  826.             if (obstacle.type == ObstacleManager.ObjectType.springy)
  827.             {
  828.                 HitSpringyObstacle(collision);
  829.             }
  830.         }
  831.  
  832.         // Cancel drift if applicable
  833.         if (shouldCancelDrift)
  834.         {
  835.             DriftCancel();
  836.         }
  837.  
  838.         // Disable speed boost visual if applicable
  839.         if (shouldCancelBoost)
  840.         {
  841.             SpeedBoostVisualDisable();
  842.         }
  843.     }
  844.  
  845.  
  846.     private void PlayCollisionAudio(Transform collisionTransform)
  847.     {
  848.         // Return early if the time since the last collision is less than 1 second
  849.         // or if the car's relative velocity is below the minimum speed threshold
  850.         if (timeSinceLastCollision < 1f || carRelativeVelocity.z < minCarSpeed)
  851.         {
  852.             return;
  853.         }
  854.  
  855.         // Check the type of object collided with and play the appropriate sound
  856.         if (collisionTransform.CompareTag("PedestrianCar"))
  857.         {
  858.             carAudioHandler.PlayPedestrianCarSound();
  859.         }
  860.         else if (collisionTransform.root.CompareTag("Player"))
  861.         {
  862.             carAudioHandler.PlayOtherPlayerCarSound();
  863.         }
  864.         else
  865.         {
  866.             carAudioHandler.PlayDefaultCrashSound();
  867.         }
  868.  
  869.         // Reset the timer for collision
  870.         timeSinceLastCollision = 0f;
  871.     }
  872.  
  873.     private void HitSpringyObstacle(Collision collision)
  874.     {
  875.         // Initiate the stunned action coroutine
  876.         StartCoroutine(StunnedAction(false, Vector3.zero, 0.2f));
  877.  
  878.         // Get the car's velocity, ignoring the y-axis, and normalize it
  879.         Vector3 carVelocity = new Vector3(carRigidbody.velocity.x, 0, carRigidbody.velocity.z).normalized;
  880.  
  881.         // Calculate the reflected vector based on the car's velocity and the collision normal
  882.         Vector3 newDir = Vector3.Reflect(carVelocity, collision.GetContact(0).normal);
  883.  
  884.         // Calculate the angle between the car's forward direction and the negative collision normal
  885.         float angle = Mathf.Deg2Rad * Vector3.Angle(transform.forward, -collision.GetContact(0).normal);
  886.  
  887.         // Compute the cosine of the angle and take its absolute value
  888.         // This will be 1 if the car hits the obstacle head-on, and 0 if it hits from the side
  889.         float x = Mathf.Abs(Mathf.Cos(angle));
  890.  
  891.         // Optional debug visualizations for car velocity, reflected direction, and collision normal
  892.         // Debug.DrawRay(transform.position, carVelocity, Color.blue, 7);
  893.         // Debug.DrawRay(collision.contacts[0].point, newDir, Color.red, 7);
  894.         // Debug.DrawRay(collision.contacts[0].point, collision.contacts[0].normal, Color.green, 7);
  895.  
  896.         // Reset the car's velocity to zero
  897.         carRigidbody.velocity = Vector3.zero;
  898.  
  899.         // Apply a force to the car in the direction of the reflected vector,
  900.         // scaled by the collision's relative velocity and a back force multiplier
  901.         carRigidbody.AddForce(x * newDir * (collision.relativeVelocity.magnitude * backForceFromSpringyObstacle));
  902.     }
  903.  
  904.     private IEnumerator HornDelay()
  905.     {
  906.         yield return new WaitForSeconds(hornDelay);
  907.         canHorn = true;
  908.     }
  909. }
  910.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement