/// <summary>
        /// The object has collided with another object.
        /// </summary>
        /// <param name="hit">The RaycastHit of the object. Can be null.</param>
        protected override void OnCollision(RaycastHit?hit)
        {
            base.OnCollision(hit);

            var forceDestruct = false;

            if (m_BounceMode == BounceMode.None)
            {
                // When there is a collision the object should move to the position that was hit so if it's not destroyed then it looks like it
                // is penetrating the hit object.
                if (hit != null && hit.HasValue && m_Collider != null)
                {
                    var closestPoint = m_Collider.ClosestPoint(hit.Value.point);
                    m_Transform.position += (hit.Value.point - closestPoint);
                    // Only set the parent to the hit transform on uniform objects to prevent stretching.
                    if (MathUtility.IsUniform(hit.Value.transform.localScale))
                    {
                        // The parent layer must be within the sticky layer mask.
                        if (MathUtility.InLayerMask(hit.Value.transform.gameObject.layer, m_StickyLayers))
                        {
                            m_Transform.parent = hit.Value.transform;

                            // If the destructible sticks to a character then the object should be added as a sub collider so collisions will be ignored.
                            m_StickyCharacterLocomotion = hit.Value.transform.gameObject.GetCachedComponent <UltimateCharacterLocomotion>();
                            if (m_StickyCharacterLocomotion != null)
                            {
                                m_StickyCharacterLocomotion.AddSubCollider(m_Collider);
                            }
                        }
                        else
                        {
                            forceDestruct = true;
                        }
                    }
                }
            }

            if (m_TrailRenderer != null)
            {
                m_TrailRenderer.enabled = false;
            }
            if (m_ParticleSystem != null)
            {
                Scheduler.ScheduleFixed(Time.fixedDeltaTime - 0.01f, StopParticleSystem);
            }

            // The object may not have been initialized before it collides.
            if (m_GameObject == null)
            {
                InitializeComponentReferences();
            }

            if (hit != null && hit.HasValue)
            {
                var hitValue      = hit.Value;
                var hitGameObject = hitValue.collider.gameObject;
                // The shield can absorb some (or none) of the damage from the destructible.
                var damageAmount = m_DamageAmount;
#if ULTIMATE_CHARACTER_CONTROLLER_MELEE
                ShieldCollider shieldCollider;
                if ((shieldCollider = hitGameObject.GetCachedComponent <ShieldCollider>()) != null)
                {
                    damageAmount = shieldCollider.Shield.Damage(this, damageAmount);
                }
#endif

                // Allow a custom event to be received.
                EventHandler.ExecuteEvent(hitGameObject, "OnObjectImpact", damageAmount, hitValue.point, hitValue.normal * m_ImpactForce, m_Originator, hitValue.collider);
                // TODO: Version 2.1.5 adds another OnObjectImpact parameter. Remove the above event later once there has been a chance to migrate over.
                EventHandler.ExecuteEvent <float, Vector3, Vector3, GameObject, object, Collider>(hitGameObject, "OnObjectImpact", damageAmount, hitValue.point, hitValue.normal * m_ImpactForce, m_Originator, this, hitValue.collider);
                if (m_OnImpactEvent != null)
                {
                    m_OnImpactEvent.Invoke(damageAmount, hitValue.point, hitValue.normal * m_ImpactForce, m_Originator);
                }

                // If the shield didn't absorb all of the damage then it should be applied to the character.
                if (damageAmount > 0)
                {
                    // If the Health component exists it will apply a force to the rigidbody in addition to deducting the health. Otherwise just apply the force to the rigidbody.
                    Health hitHealth;
                    if ((hitHealth = hitGameObject.GetCachedParentComponent <Health>()) != null)
                    {
                        hitHealth.Damage(damageAmount, hitValue.point, -hitValue.normal, m_ImpactForce, m_ImpactForceFrames, 0, m_Originator, hitValue.collider);
                    }
                    else if (m_ImpactForce > 0)
                    {
                        var collisionRigidbody = hitGameObject.GetCachedParentComponent <Rigidbody>();
                        if (collisionRigidbody != null && !collisionRigidbody.isKinematic)
                        {
                            collisionRigidbody.AddForceAtPosition(-hitValue.normal * m_ImpactForce * MathUtility.RigidbodyForceMultiplier, hitValue.point);
                        }
                        else
                        {
                            var forceObject = hitGameObject.GetCachedParentComponent <IForceObject>();
                            if (forceObject != null)
                            {
                                forceObject.AddForce(m_Transform.forward * m_ImpactForce);
                            }
                        }
                    }
                }

                // An optional state can be activated on the hit object.
                if (!string.IsNullOrEmpty(m_ImpactStateName))
                {
                    StateManager.SetState(hitGameObject, m_ImpactStateName, true);
                    // If the timer isn't -1 then the state should be disabled after a specified amount of time. If it is -1 then the state
                    // will have to be disabled manually.
                    if (m_ImpactStateDisableTimer != -1)
                    {
                        StateManager.DeactivateStateTimer(hitGameObject, m_ImpactStateName, m_ImpactStateDisableTimer);
                    }
                }
            }

            // The object can destroy itself after a small delay.
            if (m_DestroyOnCollision || forceDestruct)
            {
                Scheduler.ScheduleFixed(m_DestructionDelay, Destruct, hit);
            }
        }