/// <summary> /// The character has either landed or just left the ground. /// </summary> /// <param name="grounded">Is the character on the ground?</param> private void OnGrounded(bool grounded) { if (grounded) { if (IsActive) { StopAbility(true); } m_AirborneJumpCount = 0; m_JumpApplied = false; // Remember the land time to prevent jumping more than the JumpReoccuranceDelay. // Add the deltaTime to prevent the ability from starting during the same frame. This can happen if the character is updated within FixedUpdate // but the input is updated within Update. m_LandTime = Time.time + Time.deltaTime; m_InAirTime = -1; // Unregister the jump input within OnGrounded so a repeated jump can be applied when fall is active. if (m_AirborneJumpInput != null) { m_Handler.UnregisterInputEvent(m_AirborneJumpInput); GenericObjectPool.Return(m_AirborneJumpInput); m_AirborneJumpInput = null; } EventHandler.UnregisterEvent(m_GameObject, "OnJumpAbilityAirborneJump", OnPerformAirborneJump); m_AirborneJumpRegistered = false; } else if (!IsActive) { m_InAirTime = Time.time; } }
/// <summary> /// The GameObject has been destroyed. /// </summary> public override void OnDestroy() { base.OnDestroy(); if (m_Handler != null) { if (m_Rotate) { m_Handler.UnregisterInputEvent(m_StopRotateInputEvent); } else { m_Handler.UnregisterInputEvent(m_StartRotateInputEvent); } m_Handler.UnregisterInputEvent(m_TurnInputEvent); m_Handler.UnregisterInputEvent(m_AutoMoveInputEvent); GenericObjectPool.Return(m_StartRotateInputEvent); GenericObjectPool.Return(m_StopRotateInputEvent); GenericObjectPool.Return(m_TurnInputEvent); GenericObjectPool.Return(m_AutoMoveInputEvent); } EventHandler.UnregisterEvent(m_GameObject, "OnRPGMovementTypeStartRotate", OnStartRotate); EventHandler.UnregisterEvent(m_GameObject, "OnRPGMovementTypeStopRotate", OnStopRotate); EventHandler.UnregisterEvent <float>(m_GameObject, "OnRPGMovementTypeTurn", OnTurn); EventHandler.UnregisterEvent(m_GameObject, "OnRPGMovementTypeAutoMove", OnToggleAutoMove); }
/// <summary> /// Perform the impact action. /// </summary> /// <param name="castID">The ID of the cast.</param> /// <param name="source">The object that caused the cast.</param> /// <param name="target">The object that was hit by the cast.</param> /// <param name="hit">The raycast that caused the impact.</param> protected override void ImpactInternal(uint castID, GameObject source, GameObject target, RaycastHit hit) { var targetAttributeManager = target.GetCachedParentComponent <AttributeManager>(); if (targetAttributeManager == null) { return; } // The impact action can collide with multiple objects. Use a pooled version of the AttributeModifier for each collision. var attributeModifier = GenericObjectPool.Get <AttributeModifier>(); if (!attributeModifier.Initialize(m_AttributeModifier, targetAttributeManager)) { GenericObjectPool.Return(attributeModifier); return; } // The attribute exists. Enable the modifier. Return the modifier as soon as it is complete (which may be immediate). attributeModifier.EnableModifier(true); if (attributeModifier.AutoUpdating && attributeModifier.AutoUpdateDuration > 0) { EventHandler.RegisterEvent <AttributeModifier, bool>(attributeModifier, "OnAttributeModifierAutoUpdateEnable", ModifierAutoUpdateEnabled); } else { GenericObjectPool.Return(attributeModifier); } }
public override void ReturnToPool() { Clear(); Width = 0; Height = 0; Pool.Return(this); }
/// <summary> /// The character has either landed or just left the ground. /// </summary> /// <param name="grounded">Is the character on the ground?</param> private void OnGrounded(bool grounded) { if (grounded) { if (IsActive) { StopAbility(true); } m_AirborneJumpCount = 0; m_JumpApplied = false; // Remember the land time to prevent jumping more than the JumpReoccuranceDelay. m_LandTime = Time.time; m_InAirTime = -1; // Unregister the jump input within OnGrounded so a repeated jump can be applied when fall is active. if (m_AirborneJumpInput != null) { m_Handler.UnregisterInputEvent(m_AirborneJumpInput); GenericObjectPool.Return(m_AirborneJumpInput); m_AirborneJumpInput = null; } EventHandler.UnregisterEvent(m_GameObject, "OnJumpAbilityAirborneJump", OnAirborneJump); m_AirborneJumpRegistered = false; } else if (!IsActive) { m_InAirTime = Time.time; } }
/// <summary> /// The object has been damaged. /// </summary> /// <param name="amount">The amount of damage taken.</param> /// <param name="position">The position of the damage.</param> /// <param name="direction">The direction that the object took damage from.</param> /// <param name="forceMagnitude">The magnitude of the force that is applied to the object.</param> /// <param name="frames">The number of frames to add the force to.</param> /// <param name="radius">The radius of the explosive damage. If 0 then a non-explosive force will be used.</param> /// <param name="attacker">The GameObject that did the damage.</param> /// <param name="attackerObject">The object that did the damage.</param> /// <param name="hitCollider">The Collider that was hit.</param> public void Damage(float amount, Vector3 position, Vector3 direction, float forceMagnitude, int frames, float radius, GameObject attacker, object attackerObject, Collider hitCollider) { var pooledDamageData = GenericObjectPool.Get <DamageData>(); pooledDamageData.SetDamage(amount, position, direction, forceMagnitude, frames, radius, attacker, attackerObject, hitCollider); Damage(pooledDamageData); GenericObjectPool.Return(pooledDamageData); }
/// <summary> /// The AttributeModifier auto updater has been enabled or disabled. /// </summary> /// <param name="attributeModifier">The modifier that has been enabled or disabled.</param> /// <param name="enable">True if the modifier has been enabled.</param> private void ModifierAutoUpdateEnabled(AttributeModifier attributeModifier, bool enable) { if (enable) { return; } EventHandler.UnregisterEvent <AttributeModifier, bool>(attributeModifier, "OnAttributeModifierAutoUpdateEnable", ModifierAutoUpdateEnabled); GenericObjectPool.Return(attributeModifier); }
/// <summary> /// One or more hit indicators are shown. /// </summary> private void Update() { for (int i = m_ActiveDamageIndicatorCount - 1; i > -1; --i) { // The alpha value is determined by the amount of time the damage indicator has been visible. The indicator should be visible for a time of m_IndicatorVisiblityTime // with no fading. After m_IndicatorVisiblityTime the indicator should fade for visibilityTime. var alpha = (m_IndicatorFadeTime - (Time.time - (m_ActiveDamageIndicators[i].DisplayTime + m_IndicatorVisiblityTime))) / m_IndicatorFadeTime; if (alpha <= 0) { m_ActiveDamageIndicators[i].GameObject.SetActive(false); GenericObjectPool.Return(m_ActiveDamageIndicators[i]); m_ActiveDamageIndicatorCount--; // Sort the array so the complete indicators are at the end. for (int j = i; j < m_ActiveDamageIndicatorCount; ++j) { m_ActiveDamageIndicators[j] = m_ActiveDamageIndicators[j + 1]; } continue; } var color = m_ActiveDamageIndicators[i].Image.color; color.a = alpha; m_ActiveDamageIndicators[i].Image.color = color; var attackerPosition = (m_FollowAttacker && m_CharacterTransform != m_ActiveDamageIndicators[i].Attacker) ? m_ActiveDamageIndicators[i].Attacker.position : m_ActiveDamageIndicators[i].Position; // Adjust the hit position. var localHitPosition = m_ActiveDamageIndicators[i].Attacker.InverseTransformPoint(m_ActiveDamageIndicators[i].Position); localHitPosition.x = localHitPosition.z = 0; attackerPosition += m_ActiveDamageIndicators[i].Attacker.TransformDirection(localHitPosition); var screenPoint = m_Camera.WorldToScreenPoint(attackerPosition); var centerScreenPoint = ((new Vector2(screenPoint.x, screenPoint.y) - (new Vector2(m_Camera.pixelWidth, m_Camera.pixelHeight) / 2)) * Mathf.Sign(screenPoint.z)).normalized; var angle = Vector2.SignedAngle(centerScreenPoint, Vector2.right); m_ActiveDamageIndicators[i].Angle = 90 - angle; // Face the image in the direction of the angle. var rotation = m_ActiveDamageIndicators[i].RectTransform.localEulerAngles; rotation.z = m_ActiveDamageIndicators[i].Angle; m_ActiveDamageIndicators[i].RectTransform.localEulerAngles = rotation; // Position the indicator relative to the direction. var position = m_ActiveDamageIndicators[i].RectTransform.localPosition; position.x = Mathf.Sin(m_ActiveDamageIndicators[i].Angle * Mathf.Deg2Rad) * m_IndicatorOffset; position.y = -Mathf.Cos(m_ActiveDamageIndicators[i].Angle * Mathf.Deg2Rad) * m_IndicatorOffset; m_ActiveDamageIndicators[i].RectTransform.localPosition = position; } // The component can be disabled when the damage indicators have disappeared. if (m_ActiveDamageIndicatorCount == 0) { m_GameObject.SetActive(false); } }
/// <summary> /// The character has respawned. /// </summary> private void OnRespawn() { // No indicators should be shown when the character respawns. for (int i = m_ActiveDamageIndicatorCount - 1; i > -1; --i) { m_ActiveDamageIndicators[i].GameObject.SetActive(false); GenericObjectPool.Return(m_ActiveDamageIndicators[i]); } m_ActiveDamageIndicatorCount = 0; m_GameObject.SetActive(false); }
/// <summary> /// The action has started. /// </summary> /// <param name="origin">The location that the cast originates from.</param> public override void Start(Transform origin) { // Initialize any starting values after all of the actions have been deserialized. if (m_ColorID == 0) { m_ColorID = Shader.PropertyToID(m_ColorPropertyName); if (!m_BeginAction && m_MagicItem.BeginActions != null) { for (int i = 0; i < m_MagicItem.BeginActions.Length; ++i) { if (m_MagicItem.BeginActions[i] is FadeMaterials) { m_BeginFadeMaterials = m_MagicItem.BeginActions[i] as FadeMaterials; break; } } } } // The Object Fader should reset. EventHandler.ExecuteEvent(m_Character, "OnCharacterIndependentFade", true, true); if (m_BeginFadeMaterials == null) { // Return the previous objects. if (m_OriginalMaterialValuesMap.Count > 0) { for (int i = 0; i < m_Materials.Count; ++i) { GenericObjectPool.Return(m_OriginalMaterialValuesMap[m_Materials[i]]); m_OriginalMaterialValuesMap.Remove(m_Materials[i]); } } m_Materials.Clear(); m_ActiveMaterials.Clear(); EnableRendererFade(); } else { m_Materials = m_BeginFadeMaterials.Materials; m_ActiveMaterials = m_BeginFadeMaterials.ActiveMaterials; m_OriginalMaterialValuesMap = m_BeginFadeMaterials.OriginalMaterialValuesMap; } m_Active = true; #if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER // Update isn't called automatically for the remote players. if (m_MagicItem.NetworkInfo != null && !m_MagicItem.NetworkInfo.IsLocalPlayer()) { m_UpdateEvent = SchedulerBase.Schedule(0.001f, Update); } #endif }
/// <summary> /// One or more hit indicators are shown. /// </summary> private void Update() { for (int i = m_ActiveDamageIndicatorCount - 1; i > -1; --i) { // The alpha value is determined by the amount of time the damage indicator has been visible. The indicator should be visible for a time of m_IndicatorVisiblityTime // with no fading. After m_IndicatorVisiblityTime the indicator should fade for visibilityTime. var alpha = (m_IndicatorFadeTime - (Time.time - (m_ActiveDamageIndicators[i].DisplayTime + m_IndicatorVisiblityTime))) / m_IndicatorFadeTime; if (alpha <= 0) { m_ActiveDamageIndicators[i].GameObject.SetActive(false); GenericObjectPool.Return(m_ActiveDamageIndicators[i]); m_ActiveDamageIndicatorCount--; // Sort the array so the complete indicators are at the end. for (int j = i; j < m_ActiveDamageIndicatorCount; ++j) { m_ActiveDamageIndicators[j] = m_ActiveDamageIndicators[j + 1]; } continue; } var color = m_ActiveDamageIndicators[i].Image.color; color.a = alpha; m_ActiveDamageIndicators[i].Image.color = color; var direction = Vector3.ProjectOnPlane(m_CharacterTransform.position - ((m_FollowAttacker && m_CharacterTransform != m_ActiveDamageIndicators[i].Attacker) ? m_ActiveDamageIndicators[i].Attacker.position : m_ActiveDamageIndicators[i].Position), m_CharacterLocomotion.Up); // The hit indicator is shown on a 2D canvas so the y direction should be ignored. direction.y = 0; direction.Normalize(); var angle = Vector3.Angle(direction, m_CameraTransform.forward) * Mathf.Sign(Vector3.Dot(direction, m_CameraTransform.right)); m_ActiveDamageIndicators[i].Angle = angle; // Face the image in the direction of the angle. var rotation = m_ActiveDamageIndicators[i].RectTransform.localEulerAngles; rotation.z = -m_ActiveDamageIndicators[i].Angle; m_ActiveDamageIndicators[i].RectTransform.localEulerAngles = rotation; // Position the indicator relative to the direction. var position = m_ActiveDamageIndicators[i].RectTransform.localPosition; position.x = -Mathf.Sin(m_ActiveDamageIndicators[i].Angle * Mathf.Deg2Rad) * m_IndicatorOffset; position.y = -Mathf.Cos(m_ActiveDamageIndicators[i].Angle * Mathf.Deg2Rad) * m_IndicatorOffset; m_ActiveDamageIndicators[i].RectTransform.localPosition = position; } // The component can be disabled when the damage indicators have disappeared. if (m_ActiveDamageIndicatorCount == 0) { m_GameObject.SetActive(false); } }
/// <summary> /// The ability has stopped running. /// </summary> /// <param name="force">Was the ability force stopped?</param> protected override void AbilityStopped(bool force) { base.AbilityStopped(force); m_JumpApplied = m_Jumping = false; // Unregister for the ability input events. if (m_HoldInput != null) { m_Handler.UnregisterInputEvent(m_HoldInput); GenericObjectPool.Return(m_HoldInput); } EventHandler.UnregisterEvent(m_GameObject, "OnJumpAbilityReleaseHold", OnReleaseHold); }
/// <summary> /// Removes the obstructing material at the specified index from the ObstructingMaterials array. /// </summary> /// <param name="index">The index of the obstructing material to remove.</param> private void RemoveObstructingMaterial(int index) { var originalMaterialValue = m_OriginalMaterialValuesMap[m_ObstructingMaterials[index]]; RevertMaterial(m_ObstructingMaterials[index], originalMaterialValue); GenericObjectPool.Return(originalMaterialValue); m_OriginalMaterialValuesMap.Remove(m_ObstructingMaterials[index]); // The object is no longer faded. Move the array elements down one. for (int j = index; j < m_ObstructingMaterialsCount - 1; ++j) { m_ObstructingMaterials[j] = m_ObstructingMaterials[j + 1]; } m_ObstructingMaterialsCount--; }
/// <summary> /// Internal method which stops managing the kinematic object at the specified index. /// </summary> /// <param name="kinematicObjectIndex">The index of the kinematic object within the characters array.</param> private void UnregisterKinematicObjectInternal(int kinematicObjectIndex) { if (kinematicObjectIndex < 0) { return; } GenericObjectPool.Return(m_KinematicObjects[kinematicObjectIndex]); // Keep the array packed by shifting all of the subsequent elements over by one. for (int i = kinematicObjectIndex + 1; i < m_KinematicObjectCount; ++i) { m_KinematicObjects[i - 1] = m_KinematicObjects[i]; m_KinematicObjects[i - 1].IKinematicObject.KinematicObjectIndex = i - 1; } m_KinematicObjectCount--; }
/// <summary> /// Internal method which stops managing the camera at the specified index. /// </summary> /// <param name="characterIndex">The index of the camera within the cameras array.</param> private void UnregisterCameraInternal(int cameraIndex) { if (cameraIndex < 0) { return; } m_Cameras[cameraIndex].UnregisterCamera(); GenericObjectPool.Return(m_Cameras[cameraIndex]); // Keep the array packed by shifting all of the subsequent elements over by one. for (int i = cameraIndex + 1; i < m_CameraCount; ++i) { m_Cameras[i - 1] = m_Cameras[i]; m_Cameras[i - 1].CameraController.KinematicObjectIndex = i - 1; } m_CameraCount--; }
/// <summary> /// Internal method which stops managing the character at the specified index. /// </summary> /// <param name="characterIndex">The index of the character within the characters array.</param> private void UnregisterCharacterInternal(int characterIndex) { if (characterIndex < 0) { return; } m_Characters[characterIndex].UnregisterCharacter(); GenericObjectPool.Return(m_Characters[characterIndex]); // Keep the array packed by shifting all of the subsequent elements over by one. for (int i = characterIndex + 1; i < m_CharacterCount; ++i) { m_Characters[i - 1] = m_Characters[i]; m_Characters[i - 1].CharacterLocomotion.KinematicObjectIndex = i - 1; } m_CharacterCount--; }
/// <summary> /// The view type has changed. /// </summary> /// <param name="activate">Should the current view type be activated?</param> /// <param name="pitch">The pitch of the camera (in degrees).</param> /// <param name="yaw">The yaw of the camera (in degrees).</param> /// <param name="characterRotation">The rotation of the character.</param> public override void ChangeViewType(bool activate, float pitch, float yaw, Quaternion characterRotation) { base.ChangeViewType(activate, pitch, yaw, characterRotation); if (activate) { // Work with the handler to listen for any input events. if (m_Handler != null) { m_StartFreeMovementInputEvent = GenericObjectPool.Get <ActiveInputEvent>(); m_StartFreeMovementInputEvent.Initialize(ActiveInputEvent.Type.ButtonDown, m_CameraFreeMovementInputName, "OnRPGViewTypeStartFreeMovement"); m_StopFreeMovementInputEvent = GenericObjectPool.Get <ActiveInputEvent>(); m_StopFreeMovementInputEvent.Initialize(ActiveInputEvent.Type.ButtonUp, m_CameraFreeMovementInputName, "OnRPGViewTypeStopFreeMovement"); m_Handler.RegisterInputEvent(m_StartFreeMovementInputEvent); } EventHandler.RegisterEvent(m_GameObject, "OnRPGViewTypeStartFreeMovement", OnStartFreeMovement); EventHandler.RegisterEvent(m_GameObject, "OnRPGViewTypeStopFreeMovement", OnStopFreeMovement); EventHandler.RegisterEvent(m_Character, "OnRPGMovementTypeStartRotate", OnStartCharacterRotate); EventHandler.RegisterEvent(m_Character, "OnRPGMovementTypeStopRotate", OnStopCharacterRotate); } else { // The ViewType no longer needs to listen for input events when to ViewType is no longer active. if (m_Handler != null) { if (m_FreeMovement) { m_Handler.UnregisterAbilityInputEvent(m_StopFreeMovementInputEvent); } else { m_Handler.UnregisterAbilityInputEvent(m_StartFreeMovementInputEvent); } GenericObjectPool.Return(m_StartFreeMovementInputEvent); GenericObjectPool.Return(m_StopFreeMovementInputEvent); } EventHandler.UnregisterEvent(m_GameObject, "OnRPGViewTypeStartFreeMovement", OnStartFreeMovement); EventHandler.UnregisterEvent(m_GameObject, "OnRPGViewTypeStopFreeMovement", OnStopFreeMovement); EventHandler.UnregisterEvent(m_Character, "OnRPGMovementTypeStartRotate", OnStartCharacterRotate); EventHandler.UnregisterEvent(m_Character, "OnRPGMovementTypeStopRotate", OnStopCharacterRotate); } }
/// <summary> /// Stops managing the character. /// </summary> public void UnregisterCharacter() { if (m_CompleteInitEvent != null) { Scheduler.Cancel(m_CompleteInitEvent); m_CompleteInitEvent = null; } if (m_SmoothedBones != null) { for (int i = 0; i < m_SmoothedBones.Length; ++i) { GenericObjectPool.Return(m_SmoothedBones[i]); } m_SmoothedBones = null; } EventHandler.UnregisterEvent <ILookSource>(m_CharacterLocomotion.gameObject, "OnCharacterAttachLookSource", OnAttachLookSource); }
/// <summary> /// The ability has stopped running. /// </summary> /// <param name="force">Was the ability force stopped?</param> protected override void AbilityStopped(bool force) { base.AbilityStopped(force); // Update one last time with an axis value of 0 to return to the starting position. m_AxisValue = 0; UpdateLean(true); // The collider is no longer needed. if (m_ColliderGameObject != null) { m_ColliderGameObject.SetActive(false); } if (m_Handler != null) { m_Handler.UnregisterInputEvent(m_LeanInput); GenericObjectPool.Return(m_LeanInput); } EventHandler.UnregisterEvent <float>(m_GameObject, "OnLeanInputUpdate", OnInputUpdate); }
/// <summary> /// The view type has changed. /// </summary> /// <param name="activate">Should the current view type be activated?</param> /// <param name="pitch">The pitch of the camera (in degrees).</param> /// <param name="yaw">The yaw of the camera (in degrees).</param> /// <param name="characterRotation">The rotation of the character.</param> public override void ChangeViewType(bool activate, float pitch, float yaw, Quaternion characterRotation) { if (activate) { m_Pitch = pitch; m_Yaw = yaw; m_CharacterRotation = characterRotation; if (m_CharacterLocomotion.Platform != null) { UpdatePlatformRotationOffset(m_CharacterLocomotion.Platform); } if (m_Camera.fieldOfView != m_FieldOfView) { m_FieldOfViewChangeTime = Time.time + m_FieldOfViewDamping / m_CharacterLocomotion.TimeScale; } if (m_StepZoomSensitivity > 0) { if (m_Handler != null) { m_StepZoomInputEvent = GenericObjectPool.Get <ActiveInputEvent>(); m_StepZoomInputEvent.Initialize(ActiveInputEvent.Type.Axis, m_StepZoomInputName, "OnThirdPersonViewTypeStepZoom"); m_Handler.RegisterInputEvent(m_StepZoomInputEvent); } EventHandler.RegisterEvent <float>(m_GameObject, "OnThirdPersonViewTypeStepZoom", OnStepZoom); } } else { if (m_StepZoomSensitivity > 0) { if (m_Handler != null) { m_StepZoomInputEvent = GenericObjectPool.Get <ActiveInputEvent>(); m_Handler.UnregisterAbilityInputEvent(m_StepZoomInputEvent); GenericObjectPool.Return(m_StepZoomInputEvent); } EventHandler.UnregisterEvent <float>(m_GameObject, "OnThirdPersonViewTypeStepZoom", OnStepZoom); } } }
/// <summary> /// Perform the impact action. /// </summary> /// <param name="castID">The ID of the cast.</param> /// <param name="source">The object that caused the cast.</param> /// <param name="target">The object that was hit by the cast.</param> /// <param name="hit">The raycast that caused the impact.</param> protected override void ImpactInternal(uint castID, GameObject source, GameObject target, RaycastHit hit) { var damageTarget = DamageUtility.GetDamageTarget(target); if (damageTarget == null || !damageTarget.IsAlive()) { if (m_InterruptImpactOnNullHealth) { m_MagicItem.InterruptImpact(); } return; } var pooledDamageData = GenericObjectPool.Get <DamageData>(); pooledDamageData.SetDamage(m_Amount, source.transform.position, (source.transform.position - target.transform.position), m_ForceMagnitude, m_ForceFrames, 0, source, this, null); if (m_DamageProcessor == null) { m_DamageProcessor = DamageProcessor.Default; } m_DamageProcessor.Process(damageTarget, pooledDamageData); GenericObjectPool.Return(pooledDamageData); }
/// <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_CollisionMode == CollisionMode.Collide) { // 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.AddIgnoredCollider(m_Collider); } } else { forceDestruct = true; } } } if (m_TrailRenderer != null) { m_TrailRenderer.enabled = false; } } var destructionDelay = m_DestructionDelay; if (m_ParticleSystem != null && m_WaitForParticleStop) { destructionDelay = m_ParticleSystem.main.duration; m_ParticleSystem.Stop(true, ParticleSystemStopBehavior.StopEmitting); Stop(); } // 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 <float, Vector3, Vector3, GameObject, object, Collider>(hitGameObject, "OnObjectImpact", damageAmount, hitValue.point, m_Velocity.normalized * m_ImpactForce, m_Originator, this, hitValue.collider); if (m_OnImpactEvent != null) { m_OnImpactEvent.Invoke(damageAmount, hitValue.point, m_Velocity.normalized * 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) { var damageTarget = DamageUtility.GetDamageTarget(hitGameObject); if (damageTarget != null) { var pooledDamageData = GenericObjectPool.Get <DamageData>(); pooledDamageData.SetDamage(this, damageAmount, hitValue.point, -hitValue.normal, m_ImpactForce, m_ImpactForceFrames, 0, hitValue.collider); var damageProcessorModule = hitGameObject.GetCachedComponent <DamageProcessorModule>(); if (damageProcessorModule != null) { damageProcessorModule.ProcessDamage(m_DamageProcessor, damageTarget, pooledDamageData); } else { if (m_DamageProcessor == null) { m_DamageProcessor = DamageProcessor.Default; } m_DamageProcessor.Process(damageTarget, pooledDamageData); } GenericObjectPool.Return(pooledDamageData); } else if (m_ImpactForce > 0) { // If the damage target exists it will apply a force to the rigidbody in addition to procesing the damage. Otherwise just apply the force to the rigidbody. var collisionRigidbody = hitGameObject.GetCachedParentComponent <Rigidbody>(); if (collisionRigidbody != null && !collisionRigidbody.isKinematic) { collisionRigidbody.AddForceAtPosition(m_ImpactForce * MathUtility.RigidbodyForceMultiplier * -hitValue.normal, 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_DestroyEvent == null && (m_DestroyOnCollision || forceDestruct || destructionDelay > 0)) { m_DestroyEvent = SchedulerBase.ScheduleFixed(destructionDelay, Destruct, hit); } }
/// <summary> /// Do the explosion. /// </summary> /// <param name="damageAmount">The amount of damage to apply to the hit objects.</param> /// <param name="impactForce">The amount of force to apply to the hit object.</param> /// <param name="impactForceFrames">The number of frames to add the force to.</param> /// <param name="originator">The originator of the object.</param> public void Explode(float damageAmount, float impactForce, int impactForceFrames, GameObject originator) { Rigidbody colliderRigidbody = null; IForceObject forceObject = null; var hitCount = Physics.OverlapSphereNonAlloc(m_Transform.position, m_Radius, m_CollidersHit, m_ImpactLayers, QueryTriggerInteraction.Ignore); #if UNITY_EDITOR if (hitCount == m_MaxCollisionCount) { Debug.LogWarning("Warning: The maximum number of colliders have been hit by " + m_GameObject.name + ". Consider increasing the Max Collision Count value."); } #endif for (int i = 0; i < hitCount; ++i) { // A GameObject can contain multiple colliders. Prevent the explosion from occurring on the same GameObject multiple times. if (m_ObjectExplosions.Contains(m_CollidersHit[i].gameObject)) { continue; } m_ObjectExplosions.Add(m_CollidersHit[i].gameObject); // The base character GameObject should only be checked once. if ((forceObject = m_CollidersHit[i].gameObject.GetCachedParentComponent <IForceObject>()) != null) { if (m_ObjectExplosions.Contains(forceObject)) { continue; } m_ObjectExplosions.Add(forceObject); } // OverlapSphere can return objects that are in a different room. Perform a cast to ensure the object is within the explosion range. if (m_LineOfSight) { // Add a slight vertical offset to prevent a floor collider from getting in the way of the cast. var position = m_Transform.TransformPoint(0, 0.1f, 0); var direction = m_CollidersHit[i].transform.position - position; if (Physics.Raycast(position - direction.normalized * 0.1f, direction, out m_RaycastHit, direction.magnitude, m_ImpactLayers, QueryTriggerInteraction.Ignore) && !(m_RaycastHit.transform.IsChildOf(m_CollidersHit[i].transform) #if FIRST_PERSON_CONTROLLER // The cast should not hit any colliders who are a child of the camera. || m_RaycastHit.transform.gameObject.GetCachedParentComponent <FirstPersonController.Character.FirstPersonObjects>() != null #endif )) { // If the collider is part of a character then ensure the head can't be hit. var parentAnimator = m_CollidersHit[i].transform.gameObject.GetCachedParentComponent <Animator>(); if (parentAnimator != null && parentAnimator.isHuman) { var head = parentAnimator.GetBoneTransform(HumanBodyBones.Head); direction = head.position - position; if (Physics.Raycast(position, direction, out m_RaycastHit, direction.magnitude, m_ImpactLayers, QueryTriggerInteraction.Ignore) && !m_RaycastHit.transform.IsChildOf(m_CollidersHit[i].transform) && !m_CollidersHit[i].transform.IsChildOf(m_RaycastHit.transform) && m_RaycastHit.transform.IsChildOf(m_Transform) #if FIRST_PERSON_CONTROLLER // The cast should not hit any colliders who are a child of the camera. && m_RaycastHit.transform.gameObject.GetCachedParentComponent <FirstPersonController.Character.FirstPersonObjects>() == null #endif ) { continue; } } else { continue; } } } // The shield can absorb some (or none) of the damage from the explosion. var hitDamageAmount = damageAmount; #if ULTIMATE_CHARACTER_CONTROLLER_MELEE ShieldCollider shieldCollider; if ((shieldCollider = m_CollidersHit[i].transform.gameObject.GetCachedComponent <ShieldCollider>()) != null) { hitDamageAmount = shieldCollider.Shield.Damage(this, hitDamageAmount); } #endif // ClosestPoint only works with a subset of collider types. Vector3 closestPoint; if (m_CollidersHit[i] is BoxCollider || m_CollidersHit[i] is SphereCollider || m_CollidersHit[i] is CapsuleCollider || (m_CollidersHit[i] is MeshCollider && (m_CollidersHit[i] as MeshCollider).convex)) { closestPoint = m_CollidersHit[i].ClosestPoint(m_Transform.position); } else { closestPoint = m_CollidersHit[i].ClosestPointOnBounds(m_Transform.position); } var hitDirection = closestPoint - m_Transform.position; // Allow a custom event to be received. EventHandler.ExecuteEvent <float, Vector3, Vector3, GameObject, object, Collider>(m_CollidersHit[i].transform.gameObject, "OnObjectImpact", hitDamageAmount, closestPoint, hitDirection * m_ImpactForce, originator, this, m_CollidersHit[i]); if (m_OnImpactEvent != null) { m_OnImpactEvent.Invoke(hitDamageAmount, closestPoint, hitDirection * m_ImpactForce, originator); } // If the shield didn't absorb all of the damage then it should be applied to the character. if (hitDamageAmount > 0) { var damageTarget = DamageUtility.GetDamageTarget(m_CollidersHit[i].gameObject); if (damageTarget != null) { // If the Damage Target exists it will apply an explosive force to the character/character in addition to deducting the health. // Otherwise just apply the force to the character/rigidbody. var pooledDamageData = GenericObjectPool.Get <DamageData>(); var damageModifier = Mathf.Max(1 - (hitDirection.magnitude / m_Radius), 0.01f); pooledDamageData.SetDamage(hitDamageAmount * damageModifier, m_Transform.position, hitDirection.normalized, impactForce * damageModifier, impactForceFrames, m_Radius, originator, this, null); if (m_DamageProcessor == null) { m_DamageProcessor = DamageProcessor.Default; } m_DamageProcessor.Process(damageTarget, pooledDamageData); GenericObjectPool.Return(pooledDamageData); } else if (forceObject != null) { var damageModifier = Mathf.Max(1 - (hitDirection.magnitude / m_Radius), 0.01f); forceObject.AddForce(impactForce * damageModifier * hitDirection.normalized); } else if ((colliderRigidbody = m_CollidersHit[i].gameObject.GetCachedComponent <Rigidbody>()) != null) { colliderRigidbody.AddExplosionForce(impactForce * MathUtility.RigidbodyForceMultiplier, m_Transform.position, m_Radius); } } } m_ObjectExplosions.Clear(); // An audio clip can play when the object explodes. m_ExplosionAudioClipSet.PlayAudioClip(m_GameObject); m_DestructionEvent = SchedulerBase.Schedule(m_Lifespan, Destroy); }
public override void ReturnToPool() { Clear(); Radius = 0; Pool.Return(this); }
private void RecalculateNormals(Mesh mesh, float angle, int[] triangles, Vector3[] vertices, bool instant = false) { if (triangles == null) { if (meshInfoCache == null) { meshInfoCache = new Dictionary <Mesh, KeyValuePair <int[], Vector3[]> >(); } if (meshInfoCache.ContainsKey(mesh)) { triangles = meshInfoCache[mesh].Key; vertices = meshInfoCache[mesh].Value; } else { triangles = mesh.GetTriangles(0); vertices = mesh.vertices; meshInfoCache.Add(mesh, new KeyValuePair <int[], Vector3[]>(triangles, vertices)); } } var triNormals = AllocatedArray <Vector3> .Get(triangles.Length / 3); var normals = AllocatedArray <Vector3> .Get(vertices.Length); angle = angle * Mathf.Deg2Rad; var dictionary = PooledDictionary <Vector3, VertexEntry> .Get(vertices.Length, VectorComparer); //Goes through all the triangles and gathers up data to be used later for (var i = 0; i < triangles.Length; i += 3) { int i1 = triangles[i]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; //Calculate the normal of the triangle Vector3 p1 = vertices[i2] - vertices[i1]; Vector3 p2 = vertices[i3] - vertices[i1]; Vector3 normal = Vector3.Cross(p1, p2).normalized; int triIndex = i / 3; triNormals[triIndex] = normal; VertexEntry entry; //VertexKey key; //For each of the three points of the triangle // > Add this triangle as part of the triangles they're connected to. if (!dictionary.TryGetValue(vertices[i1], out entry)) { entry = GenericObjectPool <VertexEntry> .Get(); entry.PopulateArrays(); dictionary.Add(vertices[i1], entry); } entry.Add(i1, triIndex); if (!dictionary.TryGetValue(vertices[i2], out entry)) { entry = GenericObjectPool <VertexEntry> .Get(); entry.PopulateArrays(); dictionary.Add(vertices[i2], entry); } entry.Add(i2, triIndex); if (!dictionary.TryGetValue(vertices[i3], out entry)) { entry = GenericObjectPool <VertexEntry> .Get(); entry.PopulateArrays(); dictionary.Add(vertices[i3], entry); } entry.Add(i3, triIndex); } foreach (var kvp in dictionary) { var value = kvp.Value; for (var i = 0; i < value.Count; ++i) { var sum = new Vector3(); for (var j = 0; j < value.Count; ++j) { if (value.VertexIndex[i] == value.VertexIndex[j]) { sum += triNormals[value.TriangleIndex[j]]; } else { float dot = Vector3.Dot( triNormals[value.TriangleIndex[i]], triNormals[value.TriangleIndex[j]]); dot = Mathf.Clamp(dot, -0.99999f, 0.99999f); float acos = Mathf.Acos(dot); if (acos <= angle) { sum += triNormals[value.TriangleIndex[j]]; } } } normals[value.VertexIndex[i]] = sum.normalized; } value.Clear(); GenericObjectPool <VertexEntry> .Return(value); } dictionary.ReturnToPool(); if (instant == false) { if (mainThreadActions == null) { mainThreadActions = new Queue <System.Action>(); } mainThreadActions.Enqueue(() => { if (mesh) { mesh.normals = normals; } AllocatedArray <Vector3> .Return(normals, false); }); } else { mesh.normals = normals; AllocatedArray <Vector3> .Return(normals, false); } }
/// <summary> /// Attaches the component to the specified character. /// </summary> /// <param name="character">The handler to attach the camera to.</param> protected virtual void OnAttachCharacter(GameObject character) { enabled = character != null && !m_CameraController.ActiveViewType.FirstPersonPerspective; // Disable the fade on the previous active character. if (m_CharacterFade) { if (m_Character != null && m_Character != character) { DisableFades(); // Clear the previous mappings. if (m_CharacterFadeMaterials != null) { if (m_CacheCharacterMaterials) { for (int i = 0; i < m_CharacterFadeMaterials.Length; ++i) { if (!m_OriginalMaterialValuesMap.ContainsKey(m_CharacterFadeMaterials[i])) { continue; } GenericObjectPool.Return(m_OriginalMaterialValuesMap[m_CharacterFadeMaterials[i]]); m_OriginalMaterialValuesMap.Remove(m_CharacterFadeMaterials[i]); } } m_CharacterFadeMaterials = null; } m_OriginalMaterialValuesMap.Clear(); EventHandler.UnregisterEvent <bool>(m_Character, "OnCameraChangePerspectives", OnChangePerspectives); EventHandler.UnregisterEvent <bool, bool>(m_Character, "OnCharacterIndependentFade", OnIndependentFade); EventHandler.UnregisterEvent <Item>(m_Character, "OnInventoryAddItem", OnAddItem); EventHandler.UnregisterEvent <GameObject, bool>(m_Character, "OnShootableWeaponShowProjectile", OnShowProjectile); EventHandler.UnregisterEvent(m_Character, "OnRespawn", OnRespawn); } } m_Character = character; if (m_Character != null) { m_CharacterTransform = m_Character.transform; m_CharacterLocomotion = m_Character.GetCachedComponent <UltimateCharacterLocomotion>(); m_CharacterLayerManager = m_Character.GetCachedComponent <CharacterLayerManager>(); if (m_CharacterFade) { // Determine the number of renderers that will be faded so their materials can be cached. m_RegisteredMaterial.Clear(); var count = 0; var renderers = m_Character.GetComponentsInChildren <Renderer>(true); for (int i = 0; i < renderers.Length; ++i) { // The renderer fade can be ignored. if (renderers[i].gameObject.GetCachedComponent <IgnoreFadeIdentifier>() != null) { continue; } var materials = renderers[i].materials; for (int j = 0; j < materials.Length; ++j) { if (materials[j].HasProperty(m_ColorID)) { count++; } } } if (count > 0) { if (m_CharacterFadeMaterials == null) { m_CharacterFadeMaterials = new Material[count]; } else if (m_CharacterFadeMaterials.Length != count) { if (m_CacheCharacterMaterials) { // The mapping may exist from a previous character. for (int i = 0; i < m_CharacterFadeMaterials.Length; ++i) { GenericObjectPool.Return(m_OriginalMaterialValuesMap[m_CharacterFadeMaterials[i]]); m_OriginalMaterialValuesMap.Remove(m_CharacterFadeMaterials[i]); } } System.Array.Resize(ref m_CharacterFadeMaterials, count); } // Cache a reference to all of the faded materials. m_CharacterFadeMaterialsCount = 0; for (int i = 0; i < renderers.Length; ++i) { // The renderer fade can be ignored. if (renderers[i].gameObject.GetCachedComponent <IgnoreFadeIdentifier>() != null) { continue; } var materials = renderers[i].materials; for (int j = 0; j < materials.Length; ++j) { if (m_RegisteredMaterial.Contains(materials[j])) { continue; } if (materials[j].HasProperty(m_ColorID)) { if (materials[j].HasProperty(OriginalMaterialValue.ModeID)) { m_MaterialModeSet.Add(materials[j]); } m_CharacterFadeMaterials[m_CharacterFadeMaterialsCount] = materials[j]; m_RegisteredMaterial.Add(materials[j]); m_CharacterFadeMaterialsCount++; if (m_CacheCharacterMaterials) { var originalMaterialValues = GenericObjectPool.Get <OriginalMaterialValue>(); originalMaterialValues.Initialize(materials[j], m_ColorID, m_MaterialModeSet.Contains(materials[j])); m_OriginalMaterialValuesMap.Add(materials[j], originalMaterialValues); } } } } } EventHandler.RegisterEvent <bool>(m_Character, "OnCameraChangePerspectives", OnChangePerspectives); EventHandler.RegisterEvent <Item>(m_Character, "OnInventoryAddItem", OnAddItem); EventHandler.RegisterEvent <GameObject, bool>(m_Character, "OnShootableWeaponShowProjectile", OnShowProjectile); EventHandler.RegisterEvent <bool, bool>(m_Character, "OnCharacterIndependentFade", OnIndependentFade); EventHandler.RegisterEvent(m_Character, "OnRespawn", OnRespawn); } // Fade the obstructing objects immediately after the character has been assigned. if (m_ObstructingObjectsFade) { FadeObstructingObjects(true); } } }
public void ReturnToPool() { Pool.Return(this); }