Help! NullReferenceException: Object reference not set to an instance of an object

I’m using the karting game template provided by unity to place hold my sounds(work portfolio).
!(http://using System; using System.Collections.Generic; using KartGame.Track; using UnityEngine; using UnityEngine.Events; using Object = UnityEngine.Object; namespace KartGame.KartSystems { ///

/// This class is responsible for all aspects of the kart’s movement. It uses a kinematic rigidbody and a capsule collider /// to simulate the presence of the kart but does not use the internal physics solver and instead uses its own solver. /// The movement of the kart depends on the KartStats. These have a default value but can be adjusted by anything implementing /// the IKartModifier interface. /// [RequireComponent (typeof(IInput))] [RequireComponent (typeof(Rigidbody))] public class KartMovement : MonoBehaviour, IKartCollider, IMovable, IKartInfo { enum DriftState { NotDrifting, FacingLeft, FacingRight } [RequireInterface (typeof(IKartModifier))] [Tooltip (“A reference to the stats modification due to the type of kart being moved. This can be either a Component or ScriptableObject.”)] public Object kart; [RequireInterface (typeof(IKartModifier))] [Tooltip (“A reference to the stats modification due to the driver of kart being moved. This can be either a Component or ScriptableObject.”)] public Object driver; [RequireInterface (typeof(IKartModifier))] [Tooltip (“A reference to the stats modification due to the kart being airborne. This can be either a Component or ScriptableObject.”)] public Object airborneModifier; [RequireInterface (typeof(IInput))] [Tooltip (“A reference to the an object implementing the IInput class to be used to control the kart.”)] public Object input; [Tooltip (“A reference to a transform representing the origin of a ray to help determine if the kart is grounded. This is the front of a diamond formation.”)] public Transform frontGroundRaycast; [Tooltip (“A reference to a transform representing the origin of a ray to help determine if the kart is grounded. This is the right of a diamond formation.”)] public Transform rightGroundRaycast; [Tooltip (“A reference to a transform representing the origin of a ray to help determine if the kart is grounded. This is the left of a diamond formation.”)] public Transform leftGroundRaycast; [Tooltip (“A reference to a transform representing the origin of a ray to help determine if the kart is grounded. This is the rear of a diamond formation.”)] public Transform rearGroundRaycast; [Tooltip (“A reference to the default stats for all karts. Modifications to these are typically made using the kart and driver fields or by using the AddKartModifier function.”)] public KartStats defaultStats; [Tooltip (“The layers which represent any ground the kart can drive on.”)] public LayerMask groundLayers; [Tooltip (“The layers which represent anything the kart can collide with. This should include the ground layers.”)] public LayerMask allCollidingLayers; [Tooltip (“How fast the kart levels out when airborne.”)] public float airborneOrientationSpeed = 60f; [Tooltip (“The minimum value for the input’s steering when the kart is drifting.”)] public float minDriftingSteering = 0.2f; [Tooltip (“How fast the kart’s rotation gets corrected. This is used for smoothing the karts rotation and returning to normal driving after a drift”)] public float rotationCorrectionSpeed = 180f; [Tooltip (“The smallest allowed angle for a kart to be turned from the velocity direction in order to a drift to start.”)] public float minDriftStartAngle = 15f; [Tooltip (“The largest allowed angle for a kart to be turned from the velocity direction in order to a drift to start.”)] public float maxDriftStartAngle = 90f; [Tooltip (“When karts collide the movement is based on their weight difference and this additional velocity change.”)] public float kartToKartBump = 10f; public UnityEvent OnBecomeAirborne; public UnityEvent OnBecomeGrounded; public UnityEvent OnHop; public UnityEvent OnDriftStarted; public UnityEvent OnDriftStopped; public UnityEvent OnKartCollision; IInput m_Input; Vector3 m_RigidbodyPosition; Vector3 m_Velocity; Vector3 m_Movement; bool m_IsGrounded; GroundInfo m_CurrentGroundInfo; Rigidbody m_Rigidbody; CapsuleCollider m_Capsule; IRacer m_Racer; List m_CurrentModifiers = new List (16); // The karts stats are based on a list of modifiers. Each affects the results of the previous until the modified stats are calculated. List m_TempModifiers = new List (8); KartStats m_ModifiedStats; // The stats that are used to calculate the kart’s velocity. RaycastHit[ ] m_RaycastHitBuffer = new RaycastHit[8]; Collider[ ] m_ColliderBuffer = new Collider[8]; Quaternion m_DriftOffset = Quaternion.identity; DriftState m_DriftState; bool m_HasControl; float m_SteeringInput; float m_AccelerationInput; bool m_HopPressed; bool m_HopHeld; Vector3 m_RepositionPositionDelta; Quaternion m_RepositionRotationDelta = Quaternion.identity; const int k_MaxPenetrationSolves = 3; const float k_GroundToCapsuleOffsetDistance = 0.025f; const float k_Deadzone = 0.01f; const float k_VelocityNormalAirborneDot = 0.5f; // These properties are part of the IKartInfo interface. public Vector3 Position => m_RigidbodyPosition; public Quaternion Rotation => m_Rigidbody.rotation; public KartStats CurrentStats => m_ModifiedStats; public Vector3 Velocity => m_Velocity; public Vector3 Movement => m_Movement; public float LocalSpeed => (Quaternion.Inverse (m_Rigidbody.rotation) * Quaternion.Inverse (m_DriftOffset) * m_Velocity).z; public bool IsGrounded => m_IsGrounded; public GroundInfo CurrentGroundInfo => m_CurrentGroundInfo; void Reset () { groundLayers = LayerMask.GetMask (“Default”); allCollidingLayers = LayerMask.GetMask (“Default”); } void Start () { m_Input = input as IInput; m_Rigidbody = GetComponent (); m_Capsule = GetComponent (); m_Racer = GetComponent (); if (kart != null) m_CurrentModifiers.Add ((IKartModifier)kart); if (driver != null) m_CurrentModifiers.Add ((IKartModifier)driver); } void FixedUpdate () { if(Mathf.Approximately (Time.timeScale, 0f)) return; if (m_RepositionPositionDelta.sqrMagnitude > float.Epsilon || m_RepositionRotationDelta != Quaternion.identity) { m_Rigidbody.MovePosition (m_Rigidbody.position + m_RepositionPositionDelta); m_Rigidbody.MoveRotation (m_RepositionRotationDelta * m_Rigidbody.rotation); m_RepositionPositionDelta = Vector3.zero; m_RepositionRotationDelta = Quaternion.identity; return; } m_RigidbodyPosition = m_Rigidbody.position; KartStats.GetModifiedStats (m_CurrentModifiers, defaultStats, ref m_ModifiedStats); ClearTempModifiers (); Quaternion rotationStream = m_Rigidbody.rotation; float deltaTime = Time.deltaTime; m_CurrentGroundInfo = CheckForGround (deltaTime, rotationStream, Vector3.zero); Hop (rotationStream, m_CurrentGroundInfo); if (m_CurrentGroundInfo.isGrounded && !m_IsGrounded) OnBecomeGrounded.Invoke (); if (!m_CurrentGroundInfo.isGrounded && m_IsGrounded) OnBecomeAirborne.Invoke (); m_IsGrounded = m_CurrentGroundInfo.isGrounded; ApplyAirborneModifier (m_CurrentGroundInfo); GroundInfo nextGroundInfo = CheckForGround (deltaTime, rotationStream, m_Velocity * deltaTime); GroundNormal (deltaTime, ref rotationStream, m_CurrentGroundInfo, nextGroundInfo); TurnKart (deltaTime, ref rotationStream); StartDrift (m_CurrentGroundInfo, nextGroundInfo, rotationStream); StopDrift (deltaTime); CalculateDrivingVelocity (deltaTime, m_CurrentGroundInfo, rotationStream); Vector3 penetrationOffset = SolvePenetration (rotationStream); penetrationOffset = ProcessVelocityCollisions (deltaTime, rotationStream, penetrationOffset); rotationStream = Quaternion.RotateTowards (m_Rigidbody.rotation, rotationStream, rotationCorrectionSpeed * deltaTime); AdjustVelocityByPenetrationOffset (deltaTime, ref penetrationOffset); m_Rigidbody.MoveRotation (rotationStream); m_Rigidbody.MovePosition (m_RigidbodyPosition + m_Movement); } /// /// Removes all the temporary modifiers added through velocity collisions. They will be re-added in ProcessVelocityCollisions if they still apply. /// void ClearTempModifiers () { for (int i = 0; i < m_TempModifiers.Count; i++) { m_CurrentModifiers.Remove (m_TempModifiers); } m_TempModifiers.Clear (); } /// /// Determines how much the kart should be moved due to its collider overlapping with others. /// Vector3 SolvePenetration (Quaternion rotationStream) { Vector3 summedOffset = Vector3.zero; for (var solveIterations = 0; solveIterations < k_MaxPenetrationSolves; solveIterations++) { summedOffset = ComputePenetrationOffset (rotationStream, summedOffset); } return summedOffset; } /// /// Computes the penetration offset for a single iteration. /// /// The current rotation of the kart. /// How much the kart’s capsule is offset so far. /// How much the kart’s capsule should be offset after this solve. Vector3 ComputePenetrationOffset (Quaternion rotationStream, Vector3 summedOffset) { var capsuleAxis = rotationStream * Vector3.forward * m_Capsule.height * 0.5f; var point0 = m_RigidbodyPosition + capsuleAxis + summedOffset; var point1 = m_RigidbodyPosition - capsuleAxis + summedOffset; var kartCapsuleHitCount = Physics.OverlapCapsuleNonAlloc (point0, point1, m_Capsule.radius, m_ColliderBuffer, allCollidingLayers, QueryTriggerInteraction.Ignore); for (int i = 0; i < kartCapsuleHitCount; i++) { var hitCollider = m_ColliderBuffer; if (hitCollider == m_Capsule) continue; var hitColliderTransform = hitCollider.transform; if (Physics.ComputePenetration (m_Capsule, m_RigidbodyPosition + summedOffset, rotationStream, hitCollider, hitColliderTransform.position, hitColliderTransform.rotation, out Vector3 separationDirection, out float separationDistance)) { Vector3 offset = separationDirection * (separationDistance + Physics.defaultContactOffset); if (Mathf.Abs (offset.x) > Mathf.Abs (summedOffset.x)) summedOffset.x = offset.x; if (Mathf.Abs (offset.y) > Mathf.Abs (summedOffset.y)) summedOffset.y = offset.y; if (Mathf.Abs (offset.z) > Mathf.Abs (summedOffset.z)) summedOffset.z = offset.z; } } return summedOffset; } /// /// Checks whether or not the kart is grounded given a rotation and offset. /// /// The time between frames. /// The rotation the kart will have. /// The offset from the kart’s current position. /// Information about the ground from the offset position. GroundInfo CheckForGround (float deltaTime, Quaternion rotationStream, Vector3 offset) { GroundInfo groundInfo = new GroundInfo (); Vector3 defaultPosition = offset + m_Velocity * deltaTime; Vector3 direction = rotationStream * Vector3.down; float capsuleRadius = m_Capsule.radius; float capsuleTouchingDistance = capsuleRadius + Physics.defaultContactOffset; float groundedDistance = capsuleTouchingDistance + k_GroundToCapsuleOffsetDistance; float closeToGroundDistance = Mathf.Max (groundedDistance + capsuleRadius, m_Velocity.y); int hitCount = 0; Ray ray = new Ray (defaultPosition + frontGroundRaycast.position, direction); bool didHitFront = GetNearestFromRaycast (ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit frontHit); if (didHitFront) hitCount++; ray.origin = defaultPosition + rightGroundRaycast.position; bool didHitRight = GetNearestFromRaycast (ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit rightHit); if (didHitRight) hitCount++; ray.origin = defaultPosition + leftGroundRaycast.position; bool didHitLeft = GetNearestFromRaycast (ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit leftHit); if (didHitLeft) hitCount++; ray.origin = defaultPosition + rearGroundRaycast.position; bool didHitRear = GetNearestFromRaycast (ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit rearHit); if (didHitRear) hitCount++; groundInfo.isCapsuleTouching = frontHit.distance <= capsuleTouchingDistance || rightHit.distance <= capsuleTouchingDistance || leftHit.distance <= capsuleTouchingDistance || rearHit.distance <= capsuleTouchingDistance; groundInfo.isGrounded = frontHit.distance <= groundedDistance || rightHit.distance <= groundedDistance || leftHit.distance <= groundedDistance || rearHit.distance <= groundedDistance; groundInfo.isCloseToGround = hitCount > 0; // No hits - normal = Vector3.up if (hitCount == 0) { groundInfo.normal = Vector3.up; } // 1 hit - normal = hit.normal else if (hitCount == 1) { if (didHitFront) groundInfo.normal = frontHit.normal; else if (didHitRight) groundInfo.normal = rightHit.normal; else if (didHitLeft) groundInfo.normal = leftHit.normal; else if (didHitRear) groundInfo.normal = rearHit.normal; } // 2 hits - normal = hits average else if (hitCount == 2) { groundInfo.normal = (frontHit.normal + rightHit.normal + leftHit.normal + rearHit.normal) * 0.5f; } // 3 hits - normal = normal of plane from 3 points else if (hitCount == 3) { if (!didHitFront) groundInfo.normal = Vector3.Cross (rearHit.point - rightHit.point, leftHit.point - rightHit.point); if (!didHitRight) groundInfo.normal = Vector3.Cross (rearHit.point - frontHit.point, leftHit.point - frontHit.point); if (!didHitLeft) groundInfo.normal = Vector3.Cross (rightHit.point - frontHit.point, rearHit.point - frontHit.point); if (!didHitRear) groundInfo.normal = Vector3.Cross (leftHit.point - rightHit.point, frontHit.point - rightHit.point); } // 4 hits - normal = average of normals from 4 planes else { Vector3 normal0 = Vector3.Cross (rearHit.point - rightHit.point, leftHit.point - rightHit.point); Vector3 normal1 = Vector3.Cross (rearHit.point - frontHit.point, leftHit.point - frontHit.point); Vector3 normal2 = Vector3.Cross (rightHit.point - frontHit.point, rearHit.point - frontHit.point); Vector3 normal3 = Vector3.Cross (leftHit.point - rightHit.point, frontHit.point - rightHit.point); groundInfo.normal = (normal0 + normal1 + normal2 + normal3) * 0.25f; } if (groundInfo.isGrounded) { float dot = Vector3.Dot (groundInfo.normal, m_Velocity.normalized); if (dot > k_VelocityNormalAirborneDot) { groundInfo.isGrounded = false; } } return groundInfo; } /// /// Gets information about the nearest object hit by a raycast. /// bool GetNearestFromRaycast (Ray ray, float rayDistance, int layerMask, QueryTriggerInteraction query, out RaycastHit hit) { int hits = Physics.RaycastNonAlloc (ray, m_RaycastHitBuffer, rayDistance, layerMask, query); hit = new RaycastHit (); hit.distance = float.PositiveInfinity; bool hitSelf = false; for (int i = 0; i < hits; i++) { if (m_RaycastHitBuffer.collider == m_Capsule) { hitSelf = true; continue; } if (m_RaycastHitBuffer.distance < hit.distance) hit = m_RaycastHitBuffer; } if (hitSelf) hits–; return hits > 0; } /// /// Checks and applies the modifier to the kart’s stats if the kart is not grounded. /// void ApplyAirborneModifier (GroundInfo currentGroundInfo) { if (airborneModifier != null) { if (m_CurrentModifiers.Contains ((IKartModifier)airborneModifier) && currentGroundInfo.isGrounded) m_CurrentModifiers.Remove ((IKartModifier)airborneModifier); else if (!m_CurrentModifiers.Contains ((IKartModifier)airborneModifier) && !currentGroundInfo.isGrounded) m_CurrentModifiers.Add ((IKartModifier)airborneModifier); } } /// /// Affects the rotation stream so that the kart is level with the ground. /// void GroundNormal (float deltaTime, ref Quaternion rotationStream, GroundInfo currentGroundInfo, GroundInfo nextGroundInfo) { Vector3 rigidbodyUp = m_Rigidbody.rotation * Vector3.up; Quaternion currentTargetRotation = Quaternion.FromToRotation (rigidbodyUp, currentGroundInfo.normal); Quaternion nextTargetRotation = Quaternion.FromToRotation (rigidbodyUp, nextGroundInfo.normal); if (nextGroundInfo.isCloseToGround) rotationStream = Quaternion.RotateTowards (currentTargetRotation, nextTargetRotation, 0.5f) * rotationStream; else rotationStream = Quaternion.RotateTowards (rotationStream, nextTargetRotation, airborneOrientationSpeed * deltaTime); } /// /// Affects the rotation stream based on the steering input. /// void TurnKart (float deltaTime, ref Quaternion rotationStream) { Vector3 localVelocity = Quaternion.Inverse (rotationStream) * Quaternion.Inverse (m_DriftOffset) * m_Velocity; float forwardReverseSwitch = Mathf.Sign (localVelocity.z); float modifiedSteering = m_HasControl ? m_Input.Steering * forwardReverseSwitch : 0f; if (m_DriftState == DriftState.FacingLeft) modifiedSteering = Mathf.Clamp (modifiedSteering, -1f, -minDriftingSteering); else if (m_DriftState == DriftState.FacingRight) modifiedSteering = Mathf.Clamp (modifiedSteering, minDriftingSteering, 1f); float speedProportion = m_Velocity.sqrMagnitude > 0f ? 1f : 0f; float turn = m_ModifiedStats.turnSpeed * modifiedSteering * speedProportion * deltaTime; Quaternion deltaRotation = Quaternion.Euler (0f, turn, 0f); rotationStream = rotationStream * deltaRotation; } /// /// Calculates the velocity of the kart. /// void CalculateDrivingVelocity (float deltaTime, GroundInfo groundInfo, Quaternion rotationStream) { Vector3 localVelocity = Quaternion.Inverse (rotationStream) * Quaternion.Inverse (m_DriftOffset) * m_Velocity; if (groundInfo.isGrounded) { localVelocity.x = Mathf.MoveTowards (localVelocity.x, 0f, m_ModifiedStats.grip * deltaTime); float acceleration = m_HasControl ? m_Input.Acceleration : localVelocity.z > 0.05f ? -1f : 0f; if(acceleration > -k_Deadzone && acceleration < k_Deadzone) // No acceleration input. localVelocity.z = Mathf.MoveTowards (localVelocity.z, 0f, m_ModifiedStats.coastingDrag * deltaTime); else if (acceleration > k_Deadzone) // Positive acceleration input. localVelocity.z = Mathf.MoveTowards (localVelocity.z, m_ModifiedStats.topSpeed, acceleration * m_ModifiedStats.acceleration * deltaTime); else if (localVelocity.z > k_Deadzone) // Negative acceleration input and going forwards. localVelocity.z = Mathf.MoveTowards (localVelocity.z, 0f, -acceleration * m_ModifiedStats.braking * deltaTime); else // Negative acceleration input and not going forwards. localVelocity.z = Mathf.MoveTowards (localVelocity.z, -m_ModifiedStats.reverseSpeed, -acceleration * m_ModifiedStats.reverseAcceleration * deltaTime); } if (groundInfo.isCapsuleTouching) localVelocity.y = Mathf.Max (0f, localVelocity.y); m_Velocity = m_DriftOffset * rotationStream * localVelocity; if (!groundInfo.isCapsuleTouching) m_Velocity += Vector3.down * m_ModifiedStats.gravity * deltaTime; } /// /// Affects the velocity of the kart if it hops. /// void Hop (Quaternion rotationStream, GroundInfo currentGroundInfo) { if (currentGroundInfo.isGrounded && m_Input.HopPressed && m_HasControl) { m_Velocity += rotationStream * Vector3.up * m_ModifiedStats.hopHeight; OnHop.Invoke (); } } /// /// Starts a drift if the kart lands with a sufficient turn. /// void StartDrift (GroundInfo currentGroundInfo, GroundInfo nextGroundInfo, Quaternion rotationStream) { if (m_Input.HopHeld && !currentGroundInfo.isGrounded && nextGroundInfo.isGrounded && m_HasControl && m_DriftState == DriftState.NotDrifting) { Vector3 kartForward = rotationStream * Vector3.forward; kartForward.y = 0f; kartForward.Normalize (); Vector3 flatVelocity = m_Velocity; flatVelocity.y = 0f; flatVelocity.Normalize (); float signedAngle = Vector3.SignedAngle (kartForward, flatVelocity, Vector3.up); if (signedAngle > minDriftStartAngle && signedAngle < maxDriftStartAngle) { m_DriftOffset = Quaternion.Euler (0f, signedAngle, 0f); m_DriftState = DriftState.FacingLeft; OnDriftStarted.Invoke (); } else if (signedAngle < -minDriftStartAngle && signedAngle > -maxDriftStartAngle) { m_DriftOffset = Quaternion.Euler (0f, signedAngle, 0f); m_DriftState = DriftState.FacingRight; OnDriftStarted.Invoke (); } } } /// /// Stops a drift if the hop input is no longer held. /// void StopDrift (float deltaTime) { if (!m_Input.HopHeld || !m_HasControl) { m_DriftOffset = Quaternion.RotateTowards (m_DriftOffset, Quaternion.identity, rotationCorrectionSpeed * deltaTime); m_DriftState = DriftState.NotDrifting; OnDriftStopped.Invoke (); } } /// /// Changes the velocity of the kart and processes collisions based on the velocity of the kart. /// Vector3 ProcessVelocityCollisions (float deltaTime, Quaternion rotationStream, Vector3 penetrationOffset) { Vector3 rayDirection = m_Velocity * deltaTime + penetrationOffset; float rayLength = rayDirection.magnitude + .2f; Ray sphereCastRay = new Ray (m_RigidbodyPosition, rayDirection); int hits = Physics.SphereCastNonAlloc (sphereCastRay, 0.75f, m_RaycastHitBuffer, rayLength, allCollidingLayers, QueryTriggerInteraction.Collide); for (int i = 0; i < hits; i++) { if (m_RaycastHitBuffer.collider == m_Capsule) continue; IKartModifier kartModifier = m_RaycastHitBuffer.collider.GetComponent (); if (kartModifier != null) { m_CurrentModifiers.Add (kartModifier); m_TempModifiers.Add (kartModifier); } IKartCollider kartCollider = m_RaycastHitBuffer.collider.GetComponent (); if (Mathf.Approximately (m_RaycastHitBuffer.distance, 0f)) if (Physics.Raycast (m_RigidbodyPosition, rotationStream * Vector3.down, out RaycastHit hit, rayLength + 0.5f, allCollidingLayers, QueryTriggerInteraction.Collide)) m_RaycastHitBuffer = hit; if (kartCollider != null) { m_Velocity = kartCollider.ModifyVelocity (this, m_RaycastHitBuffer); if (Mathf.Abs(Vector3.Dot(m_RaycastHitBuffer.normal, Vector3.up)) <= .2f){ OnKartCollision.Invoke (); } } else { penetrationOffset = ComputePenetrationOffset (rotationStream, penetrationOffset); } } return penetrationOffset; } /// /// So that the velocity doesn’t keep forcing a kart into a collider, the velocity is reduced by the penetrationOffset without flipping the direction of the velocity. /// /// The time between frames. /// The amount the kart needs to be moved in order to not overlap other colliders. void AdjustVelocityByPenetrationOffset (float deltaTime, ref Vector3 penetrationOffset) { // Find how much of the velocity is in the penetration offset’s direction. Vector3 penetrationProjection = Vector3.Project (m_Velocity * deltaTime, penetrationOffset); // If the projection and offset are in opposite directions (more than 90 degrees between the velocity and offset) … if (Vector3.Dot (penetrationOffset, penetrationProjection) < 0f) { // … and if the offset is larger than the projection… if (penetrationOffset.sqrMagnitude > penetrationProjection.sqrMagnitude) { // … then reduce the velocity by the equivalent velocity of the projection and the the offset by the projection. m_Velocity -= penetrationProjection / deltaTime; penetrationOffset += penetrationProjection; } else // If the offset is smaller than the projection… { // … then reduce the velocity by the equivalent velocity of the offset and then there is the offset remaining. m_Velocity += penetrationOffset / deltaTime; penetrationOffset = Vector3.zero; } } m_Movement = m_Velocity * deltaTime + penetrationOffset; } /// /// This adds a modifier to the karts stats. This might be something like a speed boost pickup being activated. /// /// The modifier to the kart’s stats. public void AddKartModifier (IKartModifier kartModifier) { m_CurrentModifiers.Add (kartModifier); } /// /// This removes a previously added modifier to the karts stats. This might be something like a speed boost that has just run out. /// /// public void RemoveKartModifier (IKartModifier kartModifier) { m_CurrentModifiers.Remove (kartModifier); } /// /// This exists as part of the IKartCollider interface. It is called when a kart collides with this kart. /// /// The kart that has collided with this kart. /// Data for the collision. /// The velocity of the colliding kart once it has been modified. public Vector3 ModifyVelocity (IKartInfo collidingKart, RaycastHit collisionHit) { float weightDifference = collidingKart.CurrentStats.weight - m_ModifiedStats.weight; if (weightDifference <= 0f) { Vector3 toCollidingKart = (collidingKart.Position - m_RigidbodyPosition).normalized; return collidingKart.Velocity + toCollidingKart * (kartToKartBump - weightDifference); } return collidingKart.Velocity; } /// /// This exists as part of the IMovable interface. Typically it is called by the TrackManager when the race starts. /// public void EnableControl () { m_HasControl = true; } /// /// This exists as part of the IMovable interface. Typically it is called by the TrackManager when the kart finishes its final lap. /// public void DisableControl () { m_HasControl = false; m_DriftState = DriftState.NotDrifting; } /// /// This exists as part of the IMovable interface. Typically it is called by the TrackManager to determine whether control should be re-enabled after a reposition. /// /// public bool IsControlled () { return m_HasControl; } /// /// This exists as part of the IMovable interface. It is used to move the kart to a specific position for example to replace it when the kart falls off the track. /// public void ForceMove (Vector3 positionDelta, Quaternion rotationDelta) { m_Velocity = Vector3.zero; m_DriftState = DriftState.NotDrifting; m_RepositionPositionDelta = positionDelta; m_RepositionRotationDelta = rotationDelta; } /// /// This exists as part of the IMovable interface. /// /// The racer component implementation of the IRacer interface. public IRacer GetRacer () { return m_Racer; } /// /// This exists as part of the IMovable interface. /// /// The implementation of IKartInfo for this script. public IKartInfo GetKartInfo () { return this; } } }[/IMG]
The code displays two NullReferenceException: Object reference not set to an instance of an object errors
Lines 175 and 490)

using System;
using System.Collections.Generic;
using KartGame.Track;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;

namespace KartGame.KartSystems
{
    /// <summary>
    /// This class is responsible for all aspects of the kart's movement.  It uses a kinematic rigidbody and a capsule collider
    /// to simulate the presence of the kart but does not use the internal physics solver and instead uses its own solver.
    /// The  movement of the kart depends on the KartStats.  These have a default value but can be adjusted by anything implementing
    /// the IKartModifier interface.
    /// </summary>
    [RequireComponent (typeof(IInput))] [RequireComponent (typeof(Rigidbody))]
    public class KartMovement : MonoBehaviour, IKartCollider, IMovable, IKartInfo
    {
        enum DriftState
        {
            NotDrifting,
            FacingLeft,
            FacingRight
        }


        [RequireInterface (typeof(IKartModifier))]
        [Tooltip ("A reference to the stats modification due to the type of kart being moved.  This can be either a Component or ScriptableObject.")]
        public Object kart;
        [RequireInterface (typeof(IKartModifier))]
        [Tooltip ("A reference to the stats modification due to the driver of kart being moved.  This can be either a Component or ScriptableObject.")]
        public Object driver;
        [RequireInterface (typeof(IKartModifier))]
        [Tooltip ("A reference to the stats modification due to the kart being airborne.  This can be either a Component or ScriptableObject.")]
        public Object airborneModifier;
        [RequireInterface (typeof(IInput))]
        [Tooltip ("A reference to the an object implementing the IInput class to be used to control the kart.")]
        public Object input;

        [Tooltip ("A reference to a transform representing the origin of a ray to help determine if the kart is grounded.  This is the front of a diamond formation.")]
        public Transform frontGroundRaycast;
        [Tooltip ("A reference to a transform representing the origin of a ray to help determine if the kart is grounded.  This is the right of a diamond formation.")]
        public Transform rightGroundRaycast;
        [Tooltip ("A reference to a transform representing the origin of a ray to help determine if the kart is grounded.  This is the left of a diamond formation.")]
        public Transform leftGroundRaycast;
        [Tooltip ("A reference to a transform representing the origin of a ray to help determine if the kart is grounded.  This is the rear of a diamond formation.")]
        public Transform rearGroundRaycast;
        [Tooltip ("A reference to the default stats for all karts.  Modifications to these are typically made using the kart and driver fields or by using the AddKartModifier function.")]
        public KartStats defaultStats;
        [Tooltip ("The layers which represent any ground the kart can drive on.")]
        public LayerMask groundLayers;
        [Tooltip ("The layers which represent anything the kart can collide with.  This should include the ground layers.")]
        public LayerMask allCollidingLayers;
        [Tooltip ("How fast the kart levels out when airborne.")]
        public float airborneOrientationSpeed = 60f;
        [Tooltip ("The minimum value for the input's steering when the kart is drifting.")]
        public float minDriftingSteering = 0.2f;
        [Tooltip ("How fast the kart's rotation gets corrected.  This is used for smoothing the karts rotation and returning to normal driving after a drift")]
        public float rotationCorrectionSpeed = 180f;
        [Tooltip ("The smallest allowed angle for a kart to be turned from the velocity direction in order to a drift to start.")]
        public float minDriftStartAngle = 15f;
        [Tooltip ("The largest allowed angle for a kart to be turned from the velocity direction in order to a drift to start.")]
        public float maxDriftStartAngle = 90f;
        [Tooltip ("When karts collide the movement is based on their weight difference and this additional velocity change.")]
        public float kartToKartBump = 10f;

        public UnityEvent OnBecomeAirborne;
        public UnityEvent OnBecomeGrounded;
        public UnityEvent OnHop;
        public UnityEvent OnDriftStarted;
        public UnityEvent OnDriftStopped;
        public UnityEvent OnKartCollision;

        IInput m_Input;
        Vector3 m_RigidbodyPosition;
        Vector3 m_Velocity;
        Vector3 m_Movement;
        bool m_IsGrounded;
        GroundInfo m_CurrentGroundInfo;
        Rigidbody m_Rigidbody;
        CapsuleCollider m_Capsule;
        IRacer m_Racer;
        List<IKartModifier> m_CurrentModifiers = new List<IKartModifier> (16); // The karts stats are based on a list of modifiers.  Each affects the results of the previous until the modified stats are calculated.
        List<IKartModifier> m_TempModifiers = new List<IKartModifier> (8);
        KartStats m_ModifiedStats; // The stats that are used to calculate the kart's velocity.
        RaycastHit[] m_RaycastHitBuffer = new RaycastHit[8];
        Collider[] m_ColliderBuffer = new Collider[8];
        Quaternion m_DriftOffset = Quaternion.identity;
        DriftState m_DriftState;
        bool m_HasControl;
        float m_SteeringInput;
        float m_AccelerationInput;
        bool m_HopPressed;
        bool m_HopHeld;
        Vector3 m_RepositionPositionDelta;
        Quaternion m_RepositionRotationDelta = Quaternion.identity;

        const int k_MaxPenetrationSolves = 3;
        const float k_GroundToCapsuleOffsetDistance = 0.025f;
        const float k_Deadzone = 0.01f;
        const float k_VelocityNormalAirborneDot = 0.5f;

        // These properties are part of the IKartInfo interface.
        public Vector3 Position => m_RigidbodyPosition;
        public Quaternion Rotation => m_Rigidbody.rotation;
        public KartStats CurrentStats => m_ModifiedStats;
        public Vector3 Velocity => m_Velocity;
        public Vector3 Movement => m_Movement;
        public float LocalSpeed => (Quaternion.Inverse (m_Rigidbody.rotation) * Quaternion.Inverse (m_DriftOffset) * m_Velocity).z;
        public bool IsGrounded => m_IsGrounded;
        public GroundInfo CurrentGroundInfo => m_CurrentGroundInfo;

        void Reset ()
        {
            groundLayers = LayerMask.GetMask ("Default");
            allCollidingLayers = LayerMask.GetMask ("Default");
        }

        void Start ()
        {
            m_Input = input as IInput;
            m_Rigidbody = GetComponent<Rigidbody> ();
            m_Capsule = GetComponent<CapsuleCollider> ();
            m_Racer = GetComponent<IRacer> ();

            if (kart != null)
                m_CurrentModifiers.Add ((IKartModifier)kart);

            if (driver != null)
                m_CurrentModifiers.Add ((IKartModifier)driver);
        }

        void FixedUpdate ()
        {
            if(Mathf.Approximately (Time.timeScale, 0f))
                return;
           
            if (m_RepositionPositionDelta.sqrMagnitude > float.Epsilon || m_RepositionRotationDelta != Quaternion.identity)
            {
                m_Rigidbody.MovePosition (m_Rigidbody.position + m_RepositionPositionDelta);
                m_Rigidbody.MoveRotation (m_RepositionRotationDelta * m_Rigidbody.rotation);
                m_RepositionPositionDelta = Vector3.zero;
                m_RepositionRotationDelta = Quaternion.identity;
                return;
            }
           
            m_RigidbodyPosition = m_Rigidbody.position;

            KartStats.GetModifiedStats (m_CurrentModifiers, defaultStats, ref m_ModifiedStats);
            ClearTempModifiers ();

            Quaternion rotationStream = m_Rigidbody.rotation;

            float deltaTime = Time.deltaTime;
           
            m_CurrentGroundInfo = CheckForGround (deltaTime, rotationStream, Vector3.zero);
           
            Hop (rotationStream, m_CurrentGroundInfo);

            if (m_CurrentGroundInfo.isGrounded && !m_IsGrounded)
                OnBecomeGrounded.Invoke ();

            if (!m_CurrentGroundInfo.isGrounded && m_IsGrounded)
                OnBecomeAirborne.Invoke ();

            m_IsGrounded = m_CurrentGroundInfo.isGrounded;

            ApplyAirborneModifier (m_CurrentGroundInfo);

            GroundInfo nextGroundInfo = CheckForGround (deltaTime, rotationStream, m_Velocity * deltaTime);

            GroundNormal (deltaTime, ref rotationStream, m_CurrentGroundInfo, nextGroundInfo);
            TurnKart (deltaTime, ref rotationStream);

            StartDrift (m_CurrentGroundInfo, nextGroundInfo, rotationStream);
            StopDrift (deltaTime);

            CalculateDrivingVelocity (deltaTime, m_CurrentGroundInfo, rotationStream);

            Vector3 penetrationOffset = SolvePenetration (rotationStream);
            penetrationOffset = ProcessVelocityCollisions (deltaTime, rotationStream, penetrationOffset);

            rotationStream = Quaternion.RotateTowards (m_Rigidbody.rotation, rotationStream, rotationCorrectionSpeed * deltaTime);
           
            AdjustVelocityByPenetrationOffset (deltaTime, ref penetrationOffset);
           
            m_Rigidbody.MoveRotation (rotationStream);
            m_Rigidbody.MovePosition (m_RigidbodyPosition + m_Movement);
        }

        /// <summary>
        /// Removes all the temporary modifiers added through velocity collisions.  They will be re-added in ProcessVelocityCollisions if they still apply.
        /// </summary>
        void ClearTempModifiers ()
        {
            for (int i = 0; i < m_TempModifiers.Count; i++)
            {
                m_CurrentModifiers.Remove (m_TempModifiers[i]);
            }

            m_TempModifiers.Clear ();
        }

        /// <summary>
        /// Determines how much the kart should be moved due to its collider overlapping with others.
        /// </summary>
        Vector3 SolvePenetration (Quaternion rotationStream)
        {
            Vector3 summedOffset = Vector3.zero;
            for (var solveIterations = 0; solveIterations < k_MaxPenetrationSolves; solveIterations++)
            {
                summedOffset = ComputePenetrationOffset (rotationStream, summedOffset);
            }

            return summedOffset;
        }

        /// <summary>
        /// Computes the penetration offset for a single iteration.
        /// </summary>
        /// <param name="rotationStream">The current rotation of the kart.</param>
        /// <param name="summedOffset">How much the kart's capsule is offset so far.</param>
        /// <returns>How much the kart's capsule should be offset after this solve.</returns>
        Vector3 ComputePenetrationOffset (Quaternion rotationStream, Vector3 summedOffset)
        {
            var capsuleAxis = rotationStream * Vector3.forward * m_Capsule.height * 0.5f;
            var point0 = m_RigidbodyPosition + capsuleAxis + summedOffset;
            var point1 = m_RigidbodyPosition - capsuleAxis + summedOffset;
            var kartCapsuleHitCount = Physics.OverlapCapsuleNonAlloc (point0, point1, m_Capsule.radius, m_ColliderBuffer, allCollidingLayers, QueryTriggerInteraction.Ignore);

            for (int i = 0; i < kartCapsuleHitCount; i++)
            {
                var hitCollider = m_ColliderBuffer[i];
                if (hitCollider == m_Capsule)
                    continue;

                var hitColliderTransform = hitCollider.transform;
                if (Physics.ComputePenetration (m_Capsule, m_RigidbodyPosition + summedOffset, rotationStream, hitCollider, hitColliderTransform.position, hitColliderTransform.rotation, out Vector3 separationDirection, out float separationDistance))
                {
                    Vector3 offset = separationDirection * (separationDistance + Physics.defaultContactOffset);
                    if (Mathf.Abs (offset.x) > Mathf.Abs (summedOffset.x))
                        summedOffset.x = offset.x;
                    if (Mathf.Abs (offset.y) > Mathf.Abs (summedOffset.y))
                        summedOffset.y = offset.y;
                    if (Mathf.Abs (offset.z) > Mathf.Abs (summedOffset.z))
                        summedOffset.z = offset.z;
                }
            }

            return summedOffset;
        }

        /// <summary>
        /// Checks whether or not the kart is grounded given a rotation and offset.
        /// </summary>
        /// <param name="deltaTime">The time between frames.</param>
        /// <param name="rotationStream">The rotation the kart will have.</param>
        /// <param name="offset">The offset from the kart's current position.</param>
        /// <returns>Information about the ground from the offset position.</returns>
        GroundInfo CheckForGround (float deltaTime, Quaternion rotationStream, Vector3 offset)
        {
            GroundInfo groundInfo = new GroundInfo ();
            Vector3 defaultPosition = offset + m_Velocity * deltaTime;
            Vector3 direction = rotationStream * Vector3.down;

            float capsuleRadius = m_Capsule.radius;
            float capsuleTouchingDistance = capsuleRadius + Physics.defaultContactOffset;
            float groundedDistance = capsuleTouchingDistance + k_GroundToCapsuleOffsetDistance;
            float closeToGroundDistance = Mathf.Max (groundedDistance + capsuleRadius, m_Velocity.y);

            int hitCount = 0;

            Ray ray = new Ray (defaultPosition + frontGroundRaycast.position, direction);

            bool didHitFront = GetNearestFromRaycast (ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit frontHit);
            if (didHitFront)
                hitCount++;

            ray.origin = defaultPosition + rightGroundRaycast.position;

            bool didHitRight = GetNearestFromRaycast (ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit rightHit);
            if (didHitRight)
                hitCount++;

            ray.origin = defaultPosition + leftGroundRaycast.position;

            bool didHitLeft = GetNearestFromRaycast (ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit leftHit);
            if (didHitLeft)
                hitCount++;

            ray.origin = defaultPosition + rearGroundRaycast.position;

            bool didHitRear = GetNearestFromRaycast (ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit rearHit);
            if (didHitRear)
                hitCount++;

            groundInfo.isCapsuleTouching = frontHit.distance <= capsuleTouchingDistance || rightHit.distance <= capsuleTouchingDistance || leftHit.distance <= capsuleTouchingDistance || rearHit.distance <= capsuleTouchingDistance;
            groundInfo.isGrounded = frontHit.distance <= groundedDistance || rightHit.distance <= groundedDistance || leftHit.distance <= groundedDistance || rearHit.distance <= groundedDistance;
            groundInfo.isCloseToGround = hitCount > 0;

            // No hits - normal = Vector3.up
            if (hitCount == 0)
            {
                groundInfo.normal = Vector3.up;
            }

            // 1 hit - normal = hit.normal
            else if (hitCount == 1)
            {
                if (didHitFront)
                    groundInfo.normal = frontHit.normal;
                else if (didHitRight)
                    groundInfo.normal = rightHit.normal;
                else if (didHitLeft)
                    groundInfo.normal = leftHit.normal;
                else if (didHitRear)
                    groundInfo.normal = rearHit.normal;
            }

            // 2 hits - normal = hits average
            else if (hitCount == 2)
            {
                groundInfo.normal = (frontHit.normal + rightHit.normal + leftHit.normal + rearHit.normal) * 0.5f;
            }

            // 3 hits - normal = normal of plane from 3 points
            else if (hitCount == 3)
            {
                if (!didHitFront)
                    groundInfo.normal = Vector3.Cross (rearHit.point - rightHit.point, leftHit.point - rightHit.point);

                if (!didHitRight)
                    groundInfo.normal = Vector3.Cross (rearHit.point - frontHit.point, leftHit.point - frontHit.point);

                if (!didHitLeft)
                    groundInfo.normal = Vector3.Cross (rightHit.point - frontHit.point, rearHit.point - frontHit.point);

                if (!didHitRear)
                    groundInfo.normal = Vector3.Cross (leftHit.point - rightHit.point, frontHit.point - rightHit.point);
            }

            // 4 hits - normal = average of normals from 4 planes
            else
            {
                Vector3 normal0 = Vector3.Cross (rearHit.point - rightHit.point, leftHit.point - rightHit.point);
                Vector3 normal1 = Vector3.Cross (rearHit.point - frontHit.point, leftHit.point - frontHit.point);
                Vector3 normal2 = Vector3.Cross (rightHit.point - frontHit.point, rearHit.point - frontHit.point);
                Vector3 normal3 = Vector3.Cross (leftHit.point - rightHit.point, frontHit.point - rightHit.point);

                groundInfo.normal = (normal0 + normal1 + normal2 + normal3) * 0.25f;
            }

            if (groundInfo.isGrounded)
            {
                float dot = Vector3.Dot (groundInfo.normal, m_Velocity.normalized);
                if (dot > k_VelocityNormalAirborneDot)
                {
                    groundInfo.isGrounded = false;
                }
            }

            return groundInfo;
        }

        /// <summary>
        /// Gets information about the nearest object hit by a raycast.
        /// </summary>
        bool GetNearestFromRaycast (Ray ray, float rayDistance, int layerMask, QueryTriggerInteraction query, out RaycastHit hit)
        {
            int hits = Physics.RaycastNonAlloc (ray, m_RaycastHitBuffer, rayDistance, layerMask, query);

            hit = new RaycastHit ();
            hit.distance = float.PositiveInfinity;

            bool hitSelf = false;
            for (int i = 0; i < hits; i++)
            {
                if (m_RaycastHitBuffer[i].collider == m_Capsule)
                {
                    hitSelf = true;
                    continue;
                }

                if (m_RaycastHitBuffer[i].distance < hit.distance)
                    hit = m_RaycastHitBuffer[i];
            }

            if (hitSelf)
                hits--;

            return hits > 0;
        }

        /// <summary>
        /// Checks and applies the modifier to the kart's stats if the kart is not grounded.
        /// </summary>
        void ApplyAirborneModifier (GroundInfo currentGroundInfo)
        {
            if (airborneModifier != null)
            {
                if (m_CurrentModifiers.Contains ((IKartModifier)airborneModifier) && currentGroundInfo.isGrounded)
                    m_CurrentModifiers.Remove ((IKartModifier)airborneModifier);
                else if (!m_CurrentModifiers.Contains ((IKartModifier)airborneModifier) && !currentGroundInfo.isGrounded)
                    m_CurrentModifiers.Add ((IKartModifier)airborneModifier);
            }
        }

        /// <summary>
        /// Affects the rotation stream so that the kart is level with the ground.
        /// </summary>
        void GroundNormal (float deltaTime, ref Quaternion rotationStream, GroundInfo currentGroundInfo, GroundInfo nextGroundInfo)
        {
            Vector3 rigidbodyUp = m_Rigidbody.rotation * Vector3.up;
            Quaternion currentTargetRotation = Quaternion.FromToRotation (rigidbodyUp, currentGroundInfo.normal);
            Quaternion nextTargetRotation = Quaternion.FromToRotation (rigidbodyUp, nextGroundInfo.normal);
            if (nextGroundInfo.isCloseToGround)
                rotationStream = Quaternion.RotateTowards (currentTargetRotation, nextTargetRotation, 0.5f) * rotationStream;
            else
                rotationStream = Quaternion.RotateTowards (rotationStream, nextTargetRotation, airborneOrientationSpeed * deltaTime);
        }

        /// <summary>
        /// Affects the rotation stream based on the steering input.
        /// </summary>
        void TurnKart (float deltaTime, ref Quaternion rotationStream)
        {
            Vector3 localVelocity = Quaternion.Inverse (rotationStream) * Quaternion.Inverse (m_DriftOffset) * m_Velocity;
            float forwardReverseSwitch = Mathf.Sign (localVelocity.z);
            float modifiedSteering = m_HasControl ? m_Input.Steering * forwardReverseSwitch : 0f;
            if (m_DriftState == DriftState.FacingLeft)
                modifiedSteering = Mathf.Clamp (modifiedSteering, -1f, -minDriftingSteering);
            else if (m_DriftState == DriftState.FacingRight)
                modifiedSteering = Mathf.Clamp (modifiedSteering, minDriftingSteering, 1f);

            float speedProportion = m_Velocity.sqrMagnitude > 0f ? 1f : 0f;
            float turn = m_ModifiedStats.turnSpeed * modifiedSteering * speedProportion * deltaTime;
            Quaternion deltaRotation = Quaternion.Euler (0f, turn, 0f);
            rotationStream = rotationStream * deltaRotation;
        }

        /// <summary>
        /// Calculates the velocity of the kart.
        /// </summary>
        void CalculateDrivingVelocity (float deltaTime, GroundInfo groundInfo, Quaternion rotationStream)
        {
            Vector3 localVelocity = Quaternion.Inverse (rotationStream) * Quaternion.Inverse (m_DriftOffset) * m_Velocity;
            if (groundInfo.isGrounded)
            {
                localVelocity.x = Mathf.MoveTowards (localVelocity.x, 0f, m_ModifiedStats.grip * deltaTime);

                float acceleration = m_HasControl ? m_Input.Acceleration : localVelocity.z > 0.05f ? -1f : 0f;

                if(acceleration > -k_Deadzone && acceleration < k_Deadzone)    // No acceleration input.
                    localVelocity.z = Mathf.MoveTowards (localVelocity.z, 0f, m_ModifiedStats.coastingDrag * deltaTime);
                else if (acceleration > k_Deadzone)                            // Positive acceleration input.
                    localVelocity.z = Mathf.MoveTowards (localVelocity.z, m_ModifiedStats.topSpeed, acceleration * m_ModifiedStats.acceleration * deltaTime);
                else if (localVelocity.z > k_Deadzone)                         // Negative acceleration input and going forwards.
                    localVelocity.z = Mathf.MoveTowards (localVelocity.z, 0f, -acceleration * m_ModifiedStats.braking * deltaTime);
                else                                                           // Negative acceleration input and not going forwards.
                    localVelocity.z = Mathf.MoveTowards (localVelocity.z, -m_ModifiedStats.reverseSpeed, -acceleration * m_ModifiedStats.reverseAcceleration * deltaTime);
            }

            if (groundInfo.isCapsuleTouching)
                localVelocity.y = Mathf.Max (0f, localVelocity.y);

            m_Velocity = m_DriftOffset * rotationStream * localVelocity;
           
            if (!groundInfo.isCapsuleTouching)
                m_Velocity += Vector3.down * m_ModifiedStats.gravity * deltaTime;
        }

        /// <summary>
        /// Affects the velocity of the kart if it hops.
        /// </summary>
        void Hop (Quaternion rotationStream, GroundInfo currentGroundInfo)
        {
            if (currentGroundInfo.isGrounded && m_Input.HopPressed && m_HasControl)
            {
                m_Velocity += rotationStream * Vector3.up * m_ModifiedStats.hopHeight;

                OnHop.Invoke ();
            }
        }

        /// <summary>
        /// Starts a drift if the kart lands with a sufficient turn.
        /// </summary>
        void StartDrift (GroundInfo currentGroundInfo, GroundInfo nextGroundInfo, Quaternion rotationStream)
        {
            if (m_Input.HopHeld && !currentGroundInfo.isGrounded && nextGroundInfo.isGrounded && m_HasControl && m_DriftState == DriftState.NotDrifting)
            {
                Vector3 kartForward = rotationStream * Vector3.forward;
                kartForward.y = 0f;
                kartForward.Normalize ();
                Vector3 flatVelocity = m_Velocity;
                flatVelocity.y = 0f;
                flatVelocity.Normalize ();

                float signedAngle = Vector3.SignedAngle (kartForward, flatVelocity, Vector3.up);

                if (signedAngle > minDriftStartAngle && signedAngle < maxDriftStartAngle)
                {
                    m_DriftOffset = Quaternion.Euler (0f, signedAngle, 0f);
                    m_DriftState = DriftState.FacingLeft;

                    OnDriftStarted.Invoke ();
                }
                else if (signedAngle < -minDriftStartAngle && signedAngle > -maxDriftStartAngle)
                {
                    m_DriftOffset = Quaternion.Euler (0f, signedAngle, 0f);
                    m_DriftState = DriftState.FacingRight;

                    OnDriftStarted.Invoke ();
                }
            }
        }

        /// <summary>
        /// Stops a drift if the hop input is no longer held.
        /// </summary>
        void StopDrift (float deltaTime)
        {
            if (!m_Input.HopHeld || !m_HasControl)
            {
                m_DriftOffset = Quaternion.RotateTowards (m_DriftOffset, Quaternion.identity, rotationCorrectionSpeed * deltaTime);
                m_DriftState = DriftState.NotDrifting;

                OnDriftStopped.Invoke ();
            }
        }

        /// <summary>
        /// Changes the velocity of the kart and processes collisions based on the velocity of the kart.
        /// </summary>
        Vector3 ProcessVelocityCollisions (float deltaTime, Quaternion rotationStream, Vector3 penetrationOffset)
        {
            Vector3 rayDirection = m_Velocity * deltaTime + penetrationOffset;
            float rayLength = rayDirection.magnitude + .2f;
            Ray sphereCastRay = new Ray (m_RigidbodyPosition, rayDirection);
            int hits = Physics.SphereCastNonAlloc (sphereCastRay, 0.75f, m_RaycastHitBuffer, rayLength, allCollidingLayers, QueryTriggerInteraction.Collide);

            for (int i = 0; i < hits; i++)
            {
                if (m_RaycastHitBuffer[i].collider == m_Capsule)
                    continue;


                IKartModifier kartModifier = m_RaycastHitBuffer[i].collider.GetComponent<IKartModifier> ();
                if (kartModifier != null)
                {
                    m_CurrentModifiers.Add (kartModifier);
                    m_TempModifiers.Add (kartModifier);
                }

                IKartCollider kartCollider = m_RaycastHitBuffer[i].collider.GetComponent<IKartCollider> ();
                if (Mathf.Approximately (m_RaycastHitBuffer[i].distance, 0f))
                    if (Physics.Raycast (m_RigidbodyPosition, rotationStream * Vector3.down, out RaycastHit hit, rayLength + 0.5f, allCollidingLayers, QueryTriggerInteraction.Collide))
                        m_RaycastHitBuffer[i] = hit;
               
                if (kartCollider != null)
                {
                    m_Velocity = kartCollider.ModifyVelocity (this, m_RaycastHitBuffer[i]);

                    if (Mathf.Abs(Vector3.Dot(m_RaycastHitBuffer[i].normal, Vector3.up)) <= .2f){
                        OnKartCollision.Invoke ();
                    }
                }
                else
                {
                    penetrationOffset = ComputePenetrationOffset (rotationStream, penetrationOffset);
                }
            }

            return penetrationOffset;
        }

        /// <summary>
        /// So that the velocity doesn't keep forcing a kart into a collider, the velocity is reduced by the penetrationOffset without flipping the direction of the velocity.
        /// </summary>
        /// <param name="deltaTime">The time between frames.</param>
        /// <param name="penetrationOffset">The amount the kart needs to be moved in order to not overlap other colliders.</param>
        void AdjustVelocityByPenetrationOffset (float deltaTime, ref Vector3 penetrationOffset)
        {
            // Find how much of the velocity is in the penetration offset's direction.
            Vector3 penetrationProjection = Vector3.Project (m_Velocity * deltaTime, penetrationOffset);

            // If the projection and offset are in opposite directions (more than 90 degrees between the velocity and offset) ...
            if (Vector3.Dot (penetrationOffset, penetrationProjection) < 0f)
            {
                // ... and if the offset is larger than the projection...
                if (penetrationOffset.sqrMagnitude > penetrationProjection.sqrMagnitude)
                {
                    // ... then reduce the velocity by the equivalent velocity of the projection and the the offset by the projection.
                    m_Velocity -= penetrationProjection / deltaTime;
                    penetrationOffset += penetrationProjection;
                }
                else // If the offset is smaller than the projection...
                {
                    // ... then reduce the velocity by the equivalent velocity of the offset and then there is the offset remaining.
                    m_Velocity += penetrationOffset / deltaTime;
                    penetrationOffset = Vector3.zero;
                }
            }
           
            m_Movement = m_Velocity * deltaTime + penetrationOffset;
        }

        /// <summary>
        /// This adds a modifier to the karts stats.  This might be something like a speed boost pickup being activated.
        /// </summary>
        /// <param name="kartModifier">The modifier to the kart's stats.</param>
        public void AddKartModifier (IKartModifier kartModifier)
        {
            m_CurrentModifiers.Add (kartModifier);
        }

        /// <summary>
        /// This removes a previously added modifier to the karts stats.  This might be something like a speed boost that has just run out.
        /// </summary>
        /// <param name="kartModifier"></param>
        public void RemoveKartModifier (IKartModifier kartModifier)
        {
            m_CurrentModifiers.Remove (kartModifier);
        }

        /// <summary>
        /// This exists as part of the IKartCollider interface.  It is called when a kart collides with this kart.
        /// </summary>
        /// <param name="collidingKart">The kart that has collided with this kart.</param>
        /// <param name="collisionHit">Data for the collision.</param>
        /// <returns>The velocity of the colliding kart once it has been modified.</returns>
        public Vector3 ModifyVelocity (IKartInfo collidingKart, RaycastHit collisionHit)
        {
            float weightDifference = collidingKart.CurrentStats.weight - m_ModifiedStats.weight;
            if (weightDifference <= 0f)
            {
                Vector3 toCollidingKart = (collidingKart.Position - m_RigidbodyPosition).normalized;
                return collidingKart.Velocity + toCollidingKart * (kartToKartBump - weightDifference);
            }

            return collidingKart.Velocity;
        }

        /// <summary>
        /// This exists as part of the IMovable interface.  Typically it is called by the TrackManager when the race starts.
        /// </summary>
        public void EnableControl ()
        {
            m_HasControl = true;
        }

        /// <summary>
        /// This exists as part of the IMovable interface.  Typically it is called by the TrackManager when the kart finishes its final lap.
        /// </summary>
        public void DisableControl ()
        {
            m_HasControl = false;
            m_DriftState = DriftState.NotDrifting;
        }

        /// <summary>
        /// This exists as part of the IMovable interface.  Typically it is called by the TrackManager to determine whether control should be re-enabled after a reposition.
        /// </summary>
        /// <returns></returns>
        public bool IsControlled ()
        {
            return m_HasControl;
        }

        /// <summary>
        /// This exists as part of the IMovable interface.  It is used to move the kart to a specific position for example to replace it when the kart falls off the track.
        /// </summary>
        public void ForceMove (Vector3 positionDelta, Quaternion rotationDelta)
        {
            m_Velocity = Vector3.zero;
            m_DriftState = DriftState.NotDrifting;
            m_RepositionPositionDelta = positionDelta;
            m_RepositionRotationDelta = rotationDelta;
        }

        /// <summary>
        /// This exists as part of the IMovable interface.
        /// </summary>
        /// <returns>The racer component implementation of the IRacer interface.</returns>
        public IRacer GetRacer ()
        {
            return m_Racer;
        }

        /// <summary>
        /// This exists as part of the IMovable interface.
        /// </summary>
        /// <returns>The implementation of IKartInfo for this script.</returns>
        public IKartInfo GetKartInfo ()
        {
            return this;
        }
    }
}