Example #1
0
        /// <summary>
        /// Initializes the object. This will be called from an object creating the projectile (such as a weapon).
        /// </summary>
        /// <param name="velocity">The velocity to apply.</param>
        /// <param name="torque">The torque to apply.</param>
        /// <param name="damageProcessor">Processes the damage dealt to a Damage Target.</param>
        /// <param name="damageAmount">The amount of damage to apply to the hit object.</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="impactLayers">The layers that the projectile can impact with.</param>
        /// <param name="impactStateName">The name of the state to activate upon impact.</param>
        /// <param name="impactStateDisableTimer">The number of seconds until the impact state is disabled.</param>
        /// <param name="surfaceImpact">A reference to the Surface Impact triggered when the object hits an object.</param>
        /// <param name="originator">The object that instantiated the trajectory object.</param>
        public virtual void Initialize(Vector3 velocity, Vector3 torque, DamageProcessor damageProcessor, float damageAmount, float impactForce, int impactForceFrames, LayerMask impactLayers,
                                       string impactStateName, float impactStateDisableTimer, SurfaceImpact surfaceImpact, GameObject originator)
        {
            InitializeDestructibleProperties(damageProcessor, damageAmount, impactForce, impactForceFrames, impactLayers, impactStateName, impactStateDisableTimer, surfaceImpact);

            base.Initialize(velocity, torque, originator);
        }
Example #2
0
        /// <summary>
        /// Initializes the object. This will be called from an object creating the projectile (such as a weapon).
        /// </summary>
        /// <param name="velocity">The velocity to apply.</param>
        /// <param name="torque">The torque to apply.</param>
        /// <param name="damageProcessor">Processes the damage dealt to a Damage Target.</param>
        /// <param name="damageAmount">The amount of damage to apply to the hit object.</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="impactLayers">The layers that the projectile can impact with.</param>
        /// <param name="impactStateName">The name of the state to activate upon impact.</param>
        /// <param name="impactStateDisableTimer">The number of seconds until the impact state is disabled.</param>
        /// <param name="surfaceImpact">A reference to the Surface Impact triggered when the object hits an object.</param>
        /// <param name="originator">The object that instantiated the trajectory object.</param>
        public override void Initialize(Vector3 velocity, Vector3 torque, DamageProcessor damageProcessor, float damageAmount, float impactForce, int impactForceFrames, LayerMask impactLayers,
                                        string impactStateName, float impactStateDisableTimer, SurfaceImpact surfaceImpact, GameObject originator)
        {
            // The projectile can deactivate after it comes in contact with another object or after a specified amount of time. Do the scheduling here to allow
            // it to activate after a set amount of time.
            if (m_Lifespan > 0)
            {
                m_ScheduledDeactivation = SchedulerBase.Schedule(m_Lifespan, Deactivate);
            }

            base.Initialize(velocity, torque, damageProcessor, damageAmount, impactForce, impactForceFrames, impactLayers, impactStateName, impactStateDisableTimer, surfaceImpact, originator);
        }
Example #3
0
 /// <summary>
 /// Initializes the destructible properties.
 /// </summary>
 /// <param name="damageProcessor">Processes the damage dealt to a Damage Target.</param>
 /// <param name="damageAmount">The amount of damage to apply to the hit object.</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="impactLayers">The layers that the projectile can impact with.</param>
 /// <param name="impactStateName">The name of the state to activate upon impact.</param>
 /// <param name="impactStateDisableTimer">The number of seconds until the impact state is disabled.</param>
 /// <param name="surfaceImpact">A reference to the Surface Impact triggered when the object hits an object.</param>
 public void InitializeDestructibleProperties(DamageProcessor damageProcessor, float damageAmount, float impactForce, int impactForceFrames, LayerMask impactLayers, string impactStateName, float impactStateDisableTimer, SurfaceImpact surfaceImpact)
 {
     m_Destroyed = false;
     if (m_DestroyEvent != null)
     {
         SchedulerBase.Cancel(m_DestroyEvent);
         m_DestroyEvent = null;
     }
     m_DamageProcessor         = damageProcessor;
     m_DamageAmount            = damageAmount;
     m_ImpactForce             = impactForce;
     m_ImpactForceFrames       = impactForceFrames;
     m_ImpactLayers            = impactLayers;
     m_ImpactStateName         = impactStateName;
     m_ImpactStateDisableTimer = impactStateDisableTimer;
     // The SurfaceImpact may be set directly on the destructible prefab.
     if (m_SurfaceImpact == null)
     {
         m_SurfaceImpact = surfaceImpact;
     }
     if (m_TrailRenderer != null)
     {
         m_TrailRenderer.Clear();
         m_TrailRenderer.enabled = true;
     }
     if (m_ParticleSystem != null)
     {
         m_ParticleSystem.Play();
     }
     if (m_Collider != null)
     {
         m_Collider.enabled = false;
     }
     // The object may be reused and was previously stuck to a character.
     if (m_StickyCharacterLocomotion != null)
     {
         m_StickyCharacterLocomotion.RemoveIgnoredCollider(m_Collider);
         m_StickyCharacterLocomotion = null;
     }
     enabled = true;
 }
Example #4
0
        protected Unit()
        {
            _damageProcessor = new DamageProcessor(this)
            {
                DamageTaken = OnDamageTaken
            };

            var effectHandler = new EffectHandler(this);

            effectHandler.EffectChanged += OnEffectChanged;
            EffectHandler = effectHandler;

            _optionalProperties.PropertyChanged += property =>
            {
                UpdateTypes |= UnitUpdateTypes.OptionalProperty;
            };

            InitUnitProperties();

            States  = new UnitStates(this);
            _height = new Lazy <double>(() => ComputeHeight() + 1.0);
        }
Example #5
0
        /// <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);
        }
Example #6
0
        /// <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);
            }
        }
Example #7
0
        /// <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);
        }