/// <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); } }