Search Unity

How to Make power ups reusable in the karting microgame?

Discussion in 'Editor & General Support' started by vituba2202, Apr 13, 2022.

  1. vituba2202

    vituba2202

    Joined:
    Jun 8, 2021
    Posts:
    13
    Hello!
    I'm modifying unity's karting microgame and I wanted to make the speed-pads able to be used more than once because the way they are programmed makes it so that they can only be used up until the MaxTime "x" amount of seconds is reached. The player can even pick up the power-up after the MaxTime is reached but it doesn't do anything...
    I strongly suggest you download the karting microgame to have a better view of the issue but here is the code for the power-up:
    Code (CSharp):
    1. using KartGame.KartSystems;
    2. using UnityEngine;
    3. using UnityEngine.Events;
    4.  
    5. public class ArcadeKartPowerup : MonoBehaviour {
    6.  
    7.     public ArcadeKart.StatPowerup boostStats = new ArcadeKart.StatPowerup
    8.     {
    9.         MaxTime = 5f
    10.     };
    11.  
    12.     public bool isCoolingDown { get; private set; }
    13.     public float lastActivatedTimestamp { get; private set; }
    14.  
    15.     public float cooldown = 5f;
    16.  
    17.     public bool disableGameObjectWhenActivated;
    18.     public UnityEvent onPowerupActivated;
    19.     public UnityEvent onPowerupFinishCooldown;
    20.  
    21.     private void Awake()
    22.     {
    23.         lastActivatedTimestamp = -9999f;
    24.     }
    25.  
    26.  
    27.     private void Update()
    28.     {
    29.         if (isCoolingDown)
    30.         {
    31.             if (Time.time - lastActivatedTimestamp > cooldown)
    32.             {
    33.                 //finished cooldown!
    34.                 isCoolingDown = false;
    35.                 onPowerupFinishCooldown.Invoke();
    36.             }
    37.  
    38.         }
    39.     }
    40.  
    41.     private void OnTriggerEnter(Collider other)
    42.     {
    43.         if (isCoolingDown) return;
    44.  
    45.         var rb = other.attachedRigidbody;
    46.         if (rb) {
    47.  
    48.             var kart = rb.GetComponent<ArcadeKart>();
    49.  
    50.             if (kart)
    51.             {
    52.                 lastActivatedTimestamp = Time.time;
    53.                 kart.AddPowerup(this.boostStats);
    54.                 onPowerupActivated.Invoke();
    55.                 isCoolingDown = true;
    56.  
    57.                 if (disableGameObjectWhenActivated) this.gameObject.SetActive(false);
    58.             }
    59.         }
    60.     }
    61.  
    62. }
    And here is the Kart's code itself:

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using System.Collections.Generic;
    4. using UnityEngine.VFX;
    5.  
    6. namespace KartGame.KartSystems
    7. {
    8.     public class ArcadeKart : MonoBehaviour
    9.     {
    10.         [System.Serializable]
    11.         public class StatPowerup
    12.         {
    13.             public ArcadeKart.Stats modifiers;
    14.             public string PowerUpID;
    15.             public float ElapsedTime;
    16.             public float MaxTime;
    17.         }
    18.  
    19.         [System.Serializable]
    20.         public struct Stats
    21.         {
    22.             [Header("Movement Settings")]
    23.             [Min(0.001f), Tooltip("Top speed attainable when moving forward.")]
    24.             public float TopSpeed;
    25.  
    26.             [Tooltip("How quickly the kart reaches top speed.")]
    27.             public float Acceleration;
    28.  
    29.             [Min(0.001f), Tooltip("Top speed attainable when moving backward.")]
    30.             public float ReverseSpeed;
    31.  
    32.             [Tooltip("How quickly the kart reaches top speed, when moving backward.")]
    33.             public float ReverseAcceleration;
    34.  
    35.             [Tooltip("How quickly the kart starts accelerating from 0. A higher number means it accelerates faster sooner.")]
    36.             [Range(0.2f, 1)]
    37.             public float AccelerationCurve;
    38.  
    39.             [Tooltip("How quickly the kart slows down when the brake is applied.")]
    40.             public float Braking;
    41.  
    42.             [Tooltip("How quickly the kart will reach a full stop when no inputs are made.")]
    43.             public float CoastingDrag;
    44.  
    45.             [Range(0.0f, 1.0f)]
    46.             [Tooltip("The amount of side-to-side friction.")]
    47.             public float Grip;
    48.  
    49.             [Tooltip("How tightly the kart can turn left or right.")]
    50.             public float Steer;
    51.  
    52.             [Tooltip("Additional gravity for when the kart is in the air.")]
    53.             public float AddedGravity;
    54.  
    55.             // allow for stat adding for powerups.
    56.             public static Stats operator +(Stats a, Stats b)
    57.             {
    58.                 return new Stats
    59.                 {
    60.                     Acceleration        = a.Acceleration + b.Acceleration,
    61.                     AccelerationCurve   = a.AccelerationCurve + b.AccelerationCurve,
    62.                     Braking             = a.Braking + b.Braking,
    63.                     CoastingDrag        = a.CoastingDrag + b.CoastingDrag,
    64.                     AddedGravity        = a.AddedGravity + b.AddedGravity,
    65.                     Grip                = a.Grip + b.Grip,
    66.                     ReverseAcceleration = a.ReverseAcceleration + b.ReverseAcceleration,
    67.                     ReverseSpeed        = a.ReverseSpeed + b.ReverseSpeed,
    68.                     TopSpeed            = a.TopSpeed + b.TopSpeed,
    69.                     Steer               = a.Steer + b.Steer,
    70.                 };
    71.             }
    72.         }
    73.  
    74.         public Rigidbody Rigidbody { get; private set; }
    75.         public InputData Input     { get; private set; }
    76.         public float AirPercent    { get; private set; }
    77.         public float GroundPercent { get; private set; }
    78.  
    79.         public ArcadeKart.Stats baseStats = new ArcadeKart.Stats
    80.         {
    81.             TopSpeed            = 10f,
    82.             Acceleration        = 5f,
    83.             AccelerationCurve   = 4f,
    84.             Braking             = 10f,
    85.             ReverseAcceleration = 5f,
    86.             ReverseSpeed        = 5f,
    87.             Steer               = 5f,
    88.             CoastingDrag        = 4f,
    89.             Grip                = .95f,
    90.             AddedGravity        = 1f,
    91.         };
    92.  
    93.         [Header("Vehicle Visual")]
    94.         public List<GameObject> m_VisualWheels;
    95.  
    96.         [Header("Vehicle Physics")]
    97.         [Tooltip("The transform that determines the position of the kart's mass.")]
    98.         public Transform CenterOfMass;
    99.  
    100.         [Range(0.0f, 20.0f), Tooltip("Coefficient used to reorient the kart in the air. The higher the number, the faster the kart will readjust itself along the horizontal plane.")]
    101.         public float AirborneReorientationCoefficient = 3.0f;
    102.  
    103.         [Header("Drifting")]
    104.         [Range(0.01f, 1.0f), Tooltip("The grip value when drifting.")]
    105.         public float DriftGrip = 0.4f;
    106.         [Range(0.0f, 10.0f), Tooltip("Additional steer when the kart is drifting.")]
    107.         public float DriftAdditionalSteer = 5.0f;
    108.         [Range(1.0f, 30.0f), Tooltip("The higher the angle, the easier it is to regain full grip.")]
    109.         public float MinAngleToFinishDrift = 10.0f;
    110.         [Range(0.01f, 0.99f), Tooltip("Mininum speed percentage to switch back to full grip.")]
    111.         public float MinSpeedPercentToFinishDrift = 0.5f;
    112.         [Range(1.0f, 20.0f), Tooltip("The higher the value, the easier it is to control the drift steering.")]
    113.         public float DriftControl = 10.0f;
    114.         [Range(0.0f, 20.0f), Tooltip("The lower the value, the longer the drift will last without trying to control it by steering.")]
    115.         public float DriftDampening = 10.0f;
    116.  
    117.         [Header("VFX")]
    118.         [Tooltip("VFX that will be placed on the wheels when drifting.")]
    119.         public ParticleSystem DriftSparkVFX;
    120.         [Range(0.0f, 0.2f), Tooltip("Offset to displace the VFX to the side.")]
    121.         public float DriftSparkHorizontalOffset = 0.1f;
    122.         [Range(0.0f, 90.0f), Tooltip("Angle to rotate the VFX.")]
    123.         public float DriftSparkRotation = 17.0f;
    124.         [Tooltip("VFX that will be placed on the wheels when drifting.")]
    125.         public GameObject DriftTrailPrefab;
    126.         [Range(-0.1f, 0.1f), Tooltip("Vertical to move the trails up or down and ensure they are above the ground.")]
    127.         public float DriftTrailVerticalOffset;
    128.         [Tooltip("VFX that will spawn upon landing, after a jump.")]
    129.         public GameObject JumpVFX;
    130.         [Tooltip("VFX that is spawn on the nozzles of the kart.")]
    131.         public GameObject NozzleVFX;
    132.         [Tooltip("List of the kart's nozzles.")]
    133.         public List<Transform> Nozzles;
    134.  
    135.         [Header("Suspensions")]
    136.         [Tooltip("The maximum extension possible between the kart's body and the wheels.")]
    137.         [Range(0.0f, 1.0f)]
    138.         public float SuspensionHeight = 0.2f;
    139.         [Range(10.0f, 100000.0f), Tooltip("The higher the value, the stiffer the suspension will be.")]
    140.         public float SuspensionSpring = 20000.0f;
    141.         [Range(0.0f, 5000.0f), Tooltip("The higher the value, the faster the kart will stabilize itself.")]
    142.         public float SuspensionDamp = 500.0f;
    143.         [Tooltip("Vertical offset to adjust the position of the wheels relative to the kart's body.")]
    144.         [Range(-1.0f, 1.0f)]
    145.         public float WheelsPositionVerticalOffset = 0.0f;
    146.  
    147.         [Header("Physical Wheels")]
    148.         [Tooltip("The physical representations of the Kart's wheels.")]
    149.         public WheelCollider FrontLeftWheel;
    150.         public WheelCollider FrontRightWheel;
    151.         public WheelCollider RearLeftWheel;
    152.         public WheelCollider RearRightWheel;
    153.  
    154.         [Tooltip("Which layers the wheels will detect.")]
    155.         public LayerMask GroundLayers = Physics.DefaultRaycastLayers;
    156.  
    157.         // the input sources that can control the kart
    158.         IInput[] m_Inputs;
    159.  
    160.         const float k_NullInput = 0.01f;
    161.         const float k_NullSpeed = 0.01f;
    162.         Vector3 m_VerticalReference = Vector3.up;
    163.  
    164.         // Drift params
    165.         public bool WantsToDrift { get; private set; } = false;
    166.         public bool IsDrifting { get; private set; } = false;
    167.         float m_CurrentGrip = 1.0f;
    168.         float m_DriftTurningPower = 0.0f;
    169.         float m_PreviousGroundPercent = 1.0f;
    170.         readonly List<(GameObject trailRoot, WheelCollider wheel, TrailRenderer trail)> m_DriftTrailInstances = new List<(GameObject, WheelCollider, TrailRenderer)>();
    171.         readonly List<(WheelCollider wheel, float horizontalOffset, float rotation, ParticleSystem sparks)> m_DriftSparkInstances = new List<(WheelCollider, float, float, ParticleSystem)>();
    172.  
    173.         // can the kart move?
    174.         bool m_CanMove = true;
    175.         List<StatPowerup> m_ActivePowerupList = new List<StatPowerup>();
    176.         ArcadeKart.Stats m_FinalStats;
    177.  
    178.         Quaternion m_LastValidRotation;
    179.         Vector3 m_LastValidPosition;
    180.         Vector3 m_LastCollisionNormal;
    181.         bool m_HasCollision;
    182.         bool m_InAir = false;
    183.  
    184.         public void AddPowerup(StatPowerup statPowerup) => m_ActivePowerupList.Add(statPowerup);
    185.         public void SetCanMove(bool move) => m_CanMove = move;
    186.         public float GetMaxSpeed() => Mathf.Max(m_FinalStats.TopSpeed, m_FinalStats.ReverseSpeed);
    187.  
    188.         private void ActivateDriftVFX(bool active)
    189.         {
    190.             foreach (var vfx in m_DriftSparkInstances)
    191.             {
    192.                 if (active && vfx.wheel.GetGroundHit(out WheelHit hit))
    193.                 {
    194.                     if (!vfx.sparks.isPlaying)
    195.                         vfx.sparks.Play();
    196.                 }
    197.                 else
    198.                 {
    199.                     if (vfx.sparks.isPlaying)
    200.                         vfx.sparks.Stop(true, ParticleSystemStopBehavior.StopEmitting);
    201.                 }
    202.                    
    203.             }
    204.  
    205.             foreach (var trail in m_DriftTrailInstances)
    206.                 trail.Item3.emitting = active && trail.wheel.GetGroundHit(out WheelHit hit);
    207.         }
    208.  
    209.         private void UpdateDriftVFXOrientation()
    210.         {
    211.             foreach (var vfx in m_DriftSparkInstances)
    212.             {
    213.                 vfx.sparks.transform.position = vfx.wheel.transform.position - (vfx.wheel.radius * Vector3.up) + (DriftTrailVerticalOffset * Vector3.up) + (transform.right * vfx.horizontalOffset);
    214.                 vfx.sparks.transform.rotation = transform.rotation * Quaternion.Euler(0.0f, 0.0f, vfx.rotation);
    215.             }
    216.  
    217.             foreach (var trail in m_DriftTrailInstances)
    218.             {
    219.                 trail.trailRoot.transform.position = trail.wheel.transform.position - (trail.wheel.radius * Vector3.up) + (DriftTrailVerticalOffset * Vector3.up);
    220.                 trail.trailRoot.transform.rotation = transform.rotation;
    221.             }
    222.         }
    223.  
    224.         void UpdateSuspensionParams(WheelCollider wheel)
    225.         {
    226.             wheel.suspensionDistance = SuspensionHeight;
    227.             wheel.center = new Vector3(0.0f, WheelsPositionVerticalOffset, 0.0f);
    228.             JointSpring spring = wheel.suspensionSpring;
    229.             spring.spring = SuspensionSpring;
    230.             spring.damper = SuspensionDamp;
    231.             wheel.suspensionSpring = spring;
    232.         }
    233.  
    234.         void Awake()
    235.         {
    236.             Rigidbody = GetComponent<Rigidbody>();
    237.             m_Inputs = GetComponents<IInput>();
    238.  
    239.             UpdateSuspensionParams(FrontLeftWheel);
    240.             UpdateSuspensionParams(FrontRightWheel);
    241.             UpdateSuspensionParams(RearLeftWheel);
    242.             UpdateSuspensionParams(RearRightWheel);
    243.  
    244.             m_CurrentGrip = baseStats.Grip;
    245.  
    246.             if (DriftSparkVFX != null)
    247.             {
    248.                 AddSparkToWheel(RearLeftWheel, -DriftSparkHorizontalOffset, -DriftSparkRotation);
    249.                 AddSparkToWheel(RearRightWheel, DriftSparkHorizontalOffset, DriftSparkRotation);
    250.             }
    251.  
    252.             if (DriftTrailPrefab != null)
    253.             {
    254.                 AddTrailToWheel(RearLeftWheel);
    255.                 AddTrailToWheel(RearRightWheel);
    256.             }
    257.  
    258.             if (NozzleVFX != null)
    259.             {
    260.                 foreach (var nozzle in Nozzles)
    261.                 {
    262.                     Instantiate(NozzleVFX, nozzle, false);
    263.                 }
    264.             }
    265.         }
    266.  
    267.         void AddTrailToWheel(WheelCollider wheel)
    268.         {
    269.             GameObject trailRoot = Instantiate(DriftTrailPrefab, gameObject.transform, false);
    270.             TrailRenderer trail = trailRoot.GetComponentInChildren<TrailRenderer>();
    271.             trail.emitting = false;
    272.             m_DriftTrailInstances.Add((trailRoot, wheel, trail));
    273.         }
    274.  
    275.         void AddSparkToWheel(WheelCollider wheel, float horizontalOffset, float rotation)
    276.         {
    277.             GameObject vfx = Instantiate(DriftSparkVFX.gameObject, wheel.transform, false);
    278.             ParticleSystem spark = vfx.GetComponent<ParticleSystem>();
    279.             spark.Stop();
    280.             m_DriftSparkInstances.Add((wheel, horizontalOffset, -rotation, spark));
    281.         }
    282.  
    283.         void FixedUpdate()
    284.         {
    285.             UpdateSuspensionParams(FrontLeftWheel);
    286.             UpdateSuspensionParams(FrontRightWheel);
    287.             UpdateSuspensionParams(RearLeftWheel);
    288.             UpdateSuspensionParams(RearRightWheel);
    289.  
    290.             GatherInputs();
    291.  
    292.             // apply our powerups to create our finalStats
    293.             TickPowerups();
    294.  
    295.             // apply our physics properties
    296.             Rigidbody.centerOfMass = transform.InverseTransformPoint(CenterOfMass.position);
    297.  
    298.             int groundedCount = 0;
    299.             if (FrontLeftWheel.isGrounded && FrontLeftWheel.GetGroundHit(out WheelHit hit))
    300.                 groundedCount++;
    301.             if (FrontRightWheel.isGrounded && FrontRightWheel.GetGroundHit(out hit))
    302.                 groundedCount++;
    303.             if (RearLeftWheel.isGrounded && RearLeftWheel.GetGroundHit(out hit))
    304.                 groundedCount++;
    305.             if (RearRightWheel.isGrounded && RearRightWheel.GetGroundHit(out hit))
    306.                 groundedCount++;
    307.  
    308.             // calculate how grounded and airborne we are
    309.             GroundPercent = (float) groundedCount / 4.0f;
    310.             AirPercent = 1 - GroundPercent;
    311.  
    312.             // apply vehicle physics
    313.             if (m_CanMove)
    314.             {
    315.                 MoveVehicle(Input.Accelerate, Input.Brake, Input.TurnInput);
    316.             }
    317.             GroundAirbourne();
    318.  
    319.             m_PreviousGroundPercent = GroundPercent;
    320.  
    321.             UpdateDriftVFXOrientation();
    322.         }
    323.  
    324.         void GatherInputs()
    325.         {
    326.             // reset input
    327.             Input = new InputData();
    328.             WantsToDrift = false;
    329.  
    330.             // gather nonzero input from our sources
    331.             for (int i = 0; i < m_Inputs.Length; i++)
    332.             {
    333.                 Input = m_Inputs[i].GenerateInput();
    334.                 WantsToDrift = Input.Brake && Vector3.Dot(Rigidbody.velocity, transform.forward) > 0.0f;
    335.             }
    336.         }
    337.  
    338.         void TickPowerups()
    339.         {
    340.             // remove all elapsed powerups
    341.             m_ActivePowerupList.RemoveAll((p) => { return p.ElapsedTime > p.MaxTime; });
    342.  
    343.             // zero out powerups before we add them all up
    344.             var powerups = new Stats();
    345.  
    346.             // add up all our powerups
    347.             for (int i = 0; i < m_ActivePowerupList.Count; i++)
    348.             {
    349.                 var p = m_ActivePowerupList[i];
    350.  
    351.                 // add elapsed time
    352.                 p.ElapsedTime += Time.fixedDeltaTime;
    353.  
    354.                 // add up the powerups
    355.                 powerups += p.modifiers;
    356.             }
    357.  
    358.             // add powerups to our final stats
    359.             m_FinalStats = baseStats + powerups;
    360.  
    361.             // clamp values in finalstats
    362.             m_FinalStats.Grip = Mathf.Clamp(m_FinalStats.Grip, 0, 1);
    363.         }
    364.  
    365.         void GroundAirbourne()
    366.         {
    367.             // while in the air, fall faster
    368.             if (AirPercent >= 1)
    369.             {
    370.                 Rigidbody.velocity += Physics.gravity * Time.fixedDeltaTime * m_FinalStats.AddedGravity;
    371.             }
    372.         }
    373.  
    374.         public void Reset()
    375.         {
    376.             Vector3 euler = transform.rotation.eulerAngles;
    377.             euler.x = euler.z = 0f;
    378.             transform.rotation = Quaternion.Euler(euler);
    379.         }
    380.  
    381.         public float LocalSpeed()
    382.         {
    383.             if (m_CanMove)
    384.             {
    385.                 float dot = Vector3.Dot(transform.forward, Rigidbody.velocity);
    386.                 if (Mathf.Abs(dot) > 0.1f)
    387.                 {
    388.                     float speed = Rigidbody.velocity.magnitude;
    389.                     return dot < 0 ? -(speed / m_FinalStats.ReverseSpeed) : (speed / m_FinalStats.TopSpeed);
    390.                 }
    391.                 return 0f;
    392.             }
    393.             else
    394.             {
    395.                 // use this value to play kart sound when it is waiting the race start countdown.
    396.                 return Input.Accelerate ? 1.0f : 0.0f;
    397.             }
    398.         }
    399.  
    400.         void OnCollisionEnter(Collision collision) => m_HasCollision = true;
    401.         void OnCollisionExit(Collision collision) => m_HasCollision = false;
    402.  
    403.         void OnCollisionStay(Collision collision)
    404.         {
    405.             m_HasCollision = true;
    406.             m_LastCollisionNormal = Vector3.zero;
    407.             float dot = -1.0f;
    408.  
    409.             foreach (var contact in collision.contacts)
    410.             {
    411.                 if (Vector3.Dot(contact.normal, Vector3.up) > dot)
    412.                     m_LastCollisionNormal = contact.normal;
    413.             }
    414.         }
    415.  
    416.         void MoveVehicle(bool accelerate, bool brake, float turnInput)
    417.         {
    418.             float accelInput = (accelerate ? 1.0f : 0.0f) - (brake ? 1.0f : 0.0f);
    419.  
    420.             // manual acceleration curve coefficient scalar
    421.             float accelerationCurveCoeff = 5;
    422.             Vector3 localVel = transform.InverseTransformVector(Rigidbody.velocity);
    423.  
    424.             bool accelDirectionIsFwd = accelInput >= 0;
    425.             bool localVelDirectionIsFwd = localVel.z >= 0;
    426.  
    427.             // use the max speed for the direction we are going--forward or reverse.
    428.             float maxSpeed = localVelDirectionIsFwd ? m_FinalStats.TopSpeed : m_FinalStats.ReverseSpeed;
    429.             float accelPower = accelDirectionIsFwd ? m_FinalStats.Acceleration : m_FinalStats.ReverseAcceleration;
    430.  
    431.             float currentSpeed = Rigidbody.velocity.magnitude;
    432.             float accelRampT = currentSpeed / maxSpeed;
    433.             float multipliedAccelerationCurve = m_FinalStats.AccelerationCurve * accelerationCurveCoeff;
    434.             float accelRamp = Mathf.Lerp(multipliedAccelerationCurve, 1, accelRampT * accelRampT);
    435.  
    436.             bool isBraking = (localVelDirectionIsFwd && brake) || (!localVelDirectionIsFwd && accelerate);
    437.  
    438.             // if we are braking (moving reverse to where we are going)
    439.             // use the braking accleration instead
    440.             float finalAccelPower = isBraking ? m_FinalStats.Braking : accelPower;
    441.  
    442.             float finalAcceleration = finalAccelPower * accelRamp;
    443.  
    444.             // apply inputs to forward/backward
    445.             float turningPower = IsDrifting ? m_DriftTurningPower : turnInput * m_FinalStats.Steer;
    446.  
    447.             Quaternion turnAngle = Quaternion.AngleAxis(turningPower, transform.up);
    448.             Vector3 fwd = turnAngle * transform.forward;
    449.             Vector3 movement = fwd * accelInput * finalAcceleration * ((m_HasCollision || GroundPercent > 0.0f) ? 1.0f : 0.0f);
    450.  
    451.             // forward movement
    452.             bool wasOverMaxSpeed = currentSpeed >= maxSpeed;
    453.  
    454.             // if over max speed, cannot accelerate faster.
    455.             if (wasOverMaxSpeed && !isBraking)
    456.                 movement *= 0.0f;
    457.  
    458.             Vector3 newVelocity = Rigidbody.velocity + movement * Time.fixedDeltaTime;
    459.             newVelocity.y = Rigidbody.velocity.y;
    460.  
    461.             //  clamp max speed if we are on ground
    462.             if (GroundPercent > 0.0f && !wasOverMaxSpeed)
    463.             {
    464.                 newVelocity = Vector3.ClampMagnitude(newVelocity, maxSpeed);
    465.             }
    466.  
    467.             // coasting is when we aren't touching accelerate
    468.             if (Mathf.Abs(accelInput) < k_NullInput && GroundPercent > 0.0f)
    469.             {
    470.                 newVelocity = Vector3.MoveTowards(newVelocity, new Vector3(0, Rigidbody.velocity.y, 0), Time.fixedDeltaTime * m_FinalStats.CoastingDrag);
    471.             }
    472.  
    473.             Rigidbody.velocity = newVelocity;
    474.  
    475.             // Drift
    476.             if (GroundPercent > 0.0f)
    477.             {
    478.                 if (m_InAir)
    479.                 {
    480.                     m_InAir = false;
    481.                     Instantiate(JumpVFX, transform.position, Quaternion.identity);
    482.                 }
    483.  
    484.                 // manual angular velocity coefficient
    485.                 float angularVelocitySteering = 0.4f;
    486.                 float angularVelocitySmoothSpeed = 20f;
    487.  
    488.                 // turning is reversed if we're going in reverse and pressing reverse
    489.                 if (!localVelDirectionIsFwd && !accelDirectionIsFwd)
    490.                     angularVelocitySteering *= -1.0f;
    491.  
    492.                 var angularVel = Rigidbody.angularVelocity;
    493.  
    494.                 // move the Y angular velocity towards our target
    495.                 angularVel.y = Mathf.MoveTowards(angularVel.y, turningPower * angularVelocitySteering, Time.fixedDeltaTime * angularVelocitySmoothSpeed);
    496.  
    497.                 // apply the angular velocity
    498.                 Rigidbody.angularVelocity = angularVel;
    499.  
    500.                 // rotate rigidbody's velocity as well to generate immediate velocity redirection
    501.                 // manual velocity steering coefficient
    502.                 float velocitySteering = 25f;
    503.  
    504.                 // If the karts lands with a forward not in the velocity direction, we start the drift
    505.                 if (GroundPercent >= 0.0f && m_PreviousGroundPercent < 0.1f)
    506.                 {
    507.                     Vector3 flattenVelocity = Vector3.ProjectOnPlane(Rigidbody.velocity, m_VerticalReference).normalized;
    508.                     if (Vector3.Dot(flattenVelocity, transform.forward * Mathf.Sign(accelInput)) < Mathf.Cos(MinAngleToFinishDrift * Mathf.Deg2Rad))
    509.                     {
    510.                         IsDrifting = true;
    511.                         m_CurrentGrip = DriftGrip;
    512.                         m_DriftTurningPower = 0.0f;
    513.                     }
    514.                 }
    515.  
    516.                 // Drift Management
    517.                 if (!IsDrifting)
    518.                 {
    519.                     if ((WantsToDrift || isBraking) && currentSpeed > maxSpeed * MinSpeedPercentToFinishDrift)
    520.                     {
    521.                         IsDrifting = true;
    522.                         m_DriftTurningPower = turningPower + (Mathf.Sign(turningPower) * DriftAdditionalSteer);
    523.                         m_CurrentGrip = DriftGrip;
    524.  
    525.                         ActivateDriftVFX(true);
    526.                     }
    527.                 }
    528.  
    529.                 if (IsDrifting)
    530.                 {
    531.                     float turnInputAbs = Mathf.Abs(turnInput);
    532.                     if (turnInputAbs < k_NullInput)
    533.                         m_DriftTurningPower = Mathf.MoveTowards(m_DriftTurningPower, 0.0f, Mathf.Clamp01(DriftDampening * Time.fixedDeltaTime));
    534.  
    535.                     // Update the turning power based on input
    536.                     float driftMaxSteerValue = m_FinalStats.Steer + DriftAdditionalSteer;
    537.                     m_DriftTurningPower = Mathf.Clamp(m_DriftTurningPower + (turnInput * Mathf.Clamp01(DriftControl * Time.fixedDeltaTime)), -driftMaxSteerValue, driftMaxSteerValue);
    538.  
    539.                     bool facingVelocity = Vector3.Dot(Rigidbody.velocity.normalized, transform.forward * Mathf.Sign(accelInput)) > Mathf.Cos(MinAngleToFinishDrift * Mathf.Deg2Rad);
    540.  
    541.                     bool canEndDrift = true;
    542.                     if (isBraking)
    543.                         canEndDrift = false;
    544.                     else if (!facingVelocity)
    545.                         canEndDrift = false;
    546.                     else if (turnInputAbs >= k_NullInput && currentSpeed > maxSpeed * MinSpeedPercentToFinishDrift)
    547.                         canEndDrift = false;
    548.  
    549.                     if (canEndDrift || currentSpeed < k_NullSpeed)
    550.                     {
    551.                         // No Input, and car aligned with speed direction => Stop the drift
    552.                         IsDrifting = false;
    553.                         m_CurrentGrip = m_FinalStats.Grip;
    554.                     }
    555.  
    556.                 }
    557.  
    558.                 // rotate our velocity based on current steer value
    559.                 Rigidbody.velocity = Quaternion.AngleAxis(turningPower * Mathf.Sign(localVel.z) * velocitySteering * m_CurrentGrip * Time.fixedDeltaTime, transform.up) * Rigidbody.velocity;
    560.             }
    561.             else
    562.             {
    563.                 m_InAir = true;
    564.             }
    565.  
    566.             bool validPosition = false;
    567.             if (Physics.Raycast(transform.position + (transform.up * 0.1f), -transform.up, out RaycastHit hit, 3.0f, 1 << 9 | 1 << 10 | 1 << 11)) // Layer: ground (9) / Environment(10) / Track (11)
    568.             {
    569.                 Vector3 lerpVector = (m_HasCollision && m_LastCollisionNormal.y > hit.normal.y) ? m_LastCollisionNormal : hit.normal;
    570.                 m_VerticalReference = Vector3.Slerp(m_VerticalReference, lerpVector, Mathf.Clamp01(AirborneReorientationCoefficient * Time.fixedDeltaTime * (GroundPercent > 0.0f ? 10.0f : 1.0f)));    // Blend faster if on ground
    571.             }
    572.             else
    573.             {
    574.                 Vector3 lerpVector = (m_HasCollision && m_LastCollisionNormal.y > 0.0f) ? m_LastCollisionNormal : Vector3.up;
    575.                 m_VerticalReference = Vector3.Slerp(m_VerticalReference, lerpVector, Mathf.Clamp01(AirborneReorientationCoefficient * Time.fixedDeltaTime));
    576.             }
    577.  
    578.             validPosition = GroundPercent > 0.7f && !m_HasCollision && Vector3.Dot(m_VerticalReference, Vector3.up) > 0.9f;
    579.  
    580.             // Airborne / Half on ground management
    581.             if (GroundPercent < 0.7f)
    582.             {
    583.                 Rigidbody.angularVelocity = new Vector3(0.0f, Rigidbody.angularVelocity.y * 0.98f, 0.0f);
    584.                 Vector3 finalOrientationDirection = Vector3.ProjectOnPlane(transform.forward, m_VerticalReference);
    585.                 finalOrientationDirection.Normalize();
    586.                 if (finalOrientationDirection.sqrMagnitude > 0.0f)
    587.                 {
    588.                     Rigidbody.MoveRotation(Quaternion.Lerp(Rigidbody.rotation, Quaternion.LookRotation(finalOrientationDirection, m_VerticalReference), Mathf.Clamp01(AirborneReorientationCoefficient * Time.fixedDeltaTime)));
    589.                 }
    590.             }
    591.             else if (validPosition)
    592.             {
    593.                 m_LastValidPosition = transform.position;
    594.                 m_LastValidRotation.eulerAngles = new Vector3(0.0f, transform.rotation.y, 0.0f);
    595.             }
    596.  
    597.             ActivateDriftVFX(IsDrifting && GroundPercent > 0.0f);
    598.         }
    599.     }
    600. }
    I'm not sure how the kart's code is going to appear here because it is very big, but you can easily check it out by downloading the karting microgame in Unity Hub! it is very important because of the powerup script references it when setting which stats to boost.
    The idea that I had that might (or not) work is to make it so that every time the "isCollingDown" returns just add another variable that controls how long the effect lasts to the MaxTime variable, something like "MaxTime = MaxTime + EffectDuration."
    I'm sorry if there is a very obvious answer to this issue but I really don't understand a lot about scripts!
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    To have a respawnable powerup, generally you make something that sits in scene but is invisible.

    It's job is to do several things:

    - at launch, create the powerup at its location, usually from a prefab

    - observe that powerup to see when it gets collected

    - manage some kind of respawn timer so that once the powerup is collected and the necessary time passes, go back to step 1 above.

    You would need to work towards fixing that first in order to reason about the problem above.

    It is axiomatic that at a minimum you would need to understand how these powerups are processed in the current game before you can change their behavior.

    Here are some great tutorial-makers:

    Imphenzia / imphenzia - super-basic Unity tutorial:



    Jason Weimann:



    Brackeys super-basic Unity Tutorial series:



    Sebastian Lague Intro to Game Development with Unity and C#:



    Tutorials and example code are great, but keep this in mind to maximize your success and minimize your frustration:

    How to do tutorials properly, two (2) simple steps to success:

    Tutorials are a GREAT idea. Tutorials should be used this way:

    Step 1. Follow the tutorial and do every single step of the tutorial 100% precisely the way it is shown. Even the slightest deviation (even a single character!) generally ends in disaster. That's how software engineering works. Every step must be taken, every single letter must be spelled, capitalized, punctuated and spaced (or not spaced) properly, literally NOTHING can be omitted or skipped.

    Fortunately this is the easiest part to get right: Be a robot. Don't make any mistakes.
    BE PERFECT IN EVERYTHING YOU DO HERE!!

    If you get any errors, learn how to read the error code and fix your error. Google is your friend here. Do NOT continue until you fix your error. Your error will probably be somewhere near the parenthesis numbers (line and character position) in the file. It is almost CERTAINLY your typo causing the error, so look again and fix it.

    Step 2. Go back and work through every part of the tutorial again, and this time explain it to your doggie. See how I am doing that in my avatar picture? If you have no dog, explain it to your house plant. If you are unable to explain any part of it, STOP. DO NOT PROCEED. Now go learn how that part works. Read the documentation on the functions involved. Go back to the tutorial and try to figure out WHY they did that. This is the part that takes a LOT of time when you are new. It might take days or weeks to work through a single 5-minute tutorial. Stick with it. You will learn.

    Step 2 is the part everybody seems to miss. Without Step 2 you are simply a code-typing monkey and outside of the specific tutorial you did, you will be completely lost. If you want to learn, you MUST do Step 2.

    Of course, all this presupposes no errors in the tutorial. For certain tutorial makers (like Unity, Brackeys, Imphenzia, Sebastian Lague) this is usually the case. For some other less-well-known content creators, this is less true. Read the comments on the video: did anyone have issues like you did? If there's an error, you will NEVER be the first guy to find it.

    Beyond that, Step 3, 4, 5 and 6 become easy because you already understand!
     
    vituba2202 likes this.
  3. vituba2202

    vituba2202

    Joined:
    Jun 8, 2021
    Posts:
    13
    Thanks, again, for the extensive reply Kurt! I'm constantly trying to improve and this issue is not stopping me! What really irritates me is that I've already solved this problem, but I didn't save the changes and only came back the next day, so I don't remember exactly what code I wrote, only what it did.
    What the old code (the one before I deleted it) was doing was that every time the power-up went in cooldown, the MaxTime (the variable that controls how long the player is affected by the effect) was increased, thus allowing for the player to have the effects again, if they pick it up.
    I believe that it was a simple line after the "if (isCollingDown) return" in the powerup script, I didn't change anything else in the code, It was something along the lines of:

    Code (CSharp):
    1. If (isCollingDown) return;
    2.           Set MaxTime = MaxTime + Cooldown
    But I now keep getting an error because of two reasons (at least as far as I know).
    1- unity can't find a reference to the "MaxTime" value, that is on the kart script, so I need to find a way to reference that value on the kart script
    2- just placing "set" is not working, what other Type or Namespace can I use to perform this function?
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Not sure what language you got
    set a = a + b
    example from but
    set
    doesn't mean this in C#. In C#
    set
    is used for a completely different unrelated purpose.

    Just assign the variable.

    MaxTime = MaxTime + Cooldown;


    This is also very important if you are manipulating another huge project and want to make sure you don't inadvertently change some random thing and then never be able to find it again:

    Please consider using proper industrial-grade enterprise-qualified source control in order to guard and protect your hard-earned work.

    Personally I use git (completely outside of Unity) because it is free and there are tons of tutorials out there to help you set it up as well as free places to host your repo (BitBucket, Github, Gitlab, etc.).

    You can also push git repositories to other drives: thumb drives, USB drives, network drives, etc., effectively putting a complete copy of the repository there.

    As far as configuring Unity to play nice with git, keep this in mind:

    https://forum.unity.com/threads/prefab-links-keep-getting-dumped-on-git-pull.646600/#post-7142306

    Here's how I use git in one of my games, Jetpack Kurt:

    https://forum.unity.com/threads/2-steps-backwards.965048/#post-6282497

    Using fine-grained source control as you work to refine your engineering:

    https://forum.unity.com/threads/whe...grammer-example-in-text.1048739/#post-6783740

    Share/Sharing source code between projects:

    https://forum.unity.com/threads/your-techniques-to-share-code-between-projects.575959/#post-3835837

    Setting up an appropriate .gitignore file for Unity3D:

    https://forum.unity.com/threads/removing-il2cpp_cache-from-project.1084607/#post-6997067

    Generally setting Unity up (includes above .gitignore concepts):

    https://thoughtbot.com/blog/how-to-git-with-unity

    It is only simple economics that you must expend as much effort into backing it up as you feel the work is worth in the first place.

    "Use source control or you will be really sad sooner or later." - StarManta on the Unity3D forum boards
     
    vituba2202 likes this.
  5. vituba2202

    vituba2202

    Joined:
    Jun 8, 2021
    Posts:
    13
    That is a great tip, I'm downloading git as I'm writing this...
    But the issue still remains! I keep getting an error because "error CS0103 The name 'MaxTime' does not exist in the current context", I'm guessing that means I don't have a reference inside de power-up script to the MaxTime value that it was created in the kart script?
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Yes, you would need a reference to the object where it is located, and obviously it would need to be public too.

    Referencing variables, fields, methods (anything non-static) in other script instances:

    https://forum.unity.com/threads/hel...-vars-in-another-script.1076825/#post-6944639

    https://forum.unity.com/threads/accessing-a-gameobject-in-different-scene.1103239/

    REMEMBER: it isn't always the best idea for everything to access everything else all over the place. For instance, it is BAD for the player to reach into an enemy and reduce his health.

    Instead there should be a function you call on the enemy to reduce his health. All the same rules apply for the above steps: the function must be public AND you need a reference to the class instance.

    That way the enemy (and only the enemy) has code to reduce his health and simultaneously do anything else, such as kill him or make him reel from the impact, and all that code is centralized in one place.
     
  7. vituba2202

    vituba2202

    Joined:
    Jun 8, 2021
    Posts:
    13
    Hey Kurt! Sorry, I took a while to answer, but I figured it out! All I had to do was add

    Code (CSharp):
    1. boostStats.MaxTime = boostStats.MaxTime + effectDuration;
    because I already had a reference to the Kart controller script. Thank you so much for the help, I really learned a lot
     
    Kurt-Dekker likes this.
  8. tu44

    tu44

    Joined:
    Mar 15, 2022
    Posts:
    1
    In the private void OnTriggerEnter(Collider other), just reset the elapsed time when a kart touches the speedpad.

    Code (CSharp):
    1.     private void OnTriggerEnter(Collider other)
    2.     {
    3.         if (isCoolingDown) return;
    4.  
    5.         var rb = other.attachedRigidbody;
    6.         if (rb) {
    7.  
    8.             var kart = rb.GetComponent<ArcadeKart>();
    9.  
    10.             if (kart)
    11.             {
    12.                 lastActivatedTimestamp = Time.time;
    13.                 kart.AddPowerup(this.boostStats);
    14.                 onPowerupActivated.Invoke();
    15.                 isCoolingDown = true;
    16.  
    17.                 if(boostStats.ElapsedTime != 0){
    18.                     boostStats.ElapsedTime = 0;
    19.                 }
    20.                 if (disableGameObjectWhenActivated) this.gameObject.SetActive(false);
    21.             }
    22.         }
    23.     }
    This should make it able to trigger speedups multiple times
     
    Last edited: Apr 27, 2022
    vituba2202 likes this.
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    While it is always awesome to have new folks chiming in, NOBODY is going to even glance at your misformatted code post above. I can't even tell what's part of the
    if
    or even what is going on. It's just a wall of blah.

    If you post a code snippet, ALWAYS USE CODE TAGS:

    How to use code tags: https://forum.unity.com/threads/using-code-tags-properly.143875/

    You may edit your post above.