/// <summary> /// Fade out the decals in the decals to fade list. /// </summary> private void Update() { for (int i = m_DecalsToFade.Count - 1; i >= 0; --i) { if (m_DecalsToFade[i] == null) { m_DecalsToFade.RemoveAt(i); continue; } var color = m_DecalsToFade[i].material.color; color.a = Mathf.Lerp(color.a, 0, Time.deltaTime * m_RemoveFadeoutSpeed); // The decal can be removed from the list when it is completely faded out. if (color.a == 0) { ObjectPoolBase.Destroy(m_DecalsToFade[i].gameObject); m_DecalsToFade.RemoveAt(i); } else { m_DecalsToFade[i].material.color = color; } } // The component can be disabled when there are no decals within the list. if (m_DecalsToFade.Count == 0) { enabled = false; } }
/// <summary> /// Decrease the alpha value of the muzzle flash to give it a fading effect. As soon as the alpha value reaches zero place the muzzle flash back in /// the object pool. If a light exists decrease the intensity of the light as well. /// </summary> private void Update() { if (m_Color.a > 0) { m_Color.a = Mathf.Max(m_Color.a - (m_FadeSpeed * Time.deltaTime * m_TimeScale), 0); if (m_Material != null) { m_Material.SetColor(m_TintColorPropertyID, m_Color); } // Keep the light intensity synchronized with the alpha channel's value. if (m_Light != null) { m_Light.intensity = m_StartLightIntensity * (m_Color.a / m_StartAlpha); } } else { if (m_Pooled) { ObjectPoolBase.Destroy(m_GameObject); } else { m_GameObject.SetActive(false); } } }
/// <summary> /// Initialize the default values. /// </summary> protected override void Awake() { base.Awake(); m_TrajectoryObject = GetComponent <TrajectoryObject>(); m_CharacterLocomotion = m_Character.GetCachedComponent <UltimateCharacterLocomotion>(); m_CharacterTransform = m_CharacterLocomotion.transform; #if ULTIMATE_CHARACTER_CONTROLLER_VR m_VRThrowableItem = GetComponent <IVRThrowableItem>(); #endif if (m_ThrownObject != null && m_TrajectoryObject != null) { // The object has to be instantiated for GetComponent to work. var instantiatedThrownObject = ObjectPoolBase.Instantiate(m_ThrownObject); var trajectoryCollider = instantiatedThrownObject.GetComponent <Collider>(); if (trajectoryCollider != null) { // Only sphere and capsules are supported. if (trajectoryCollider is SphereCollider) { var trajectorySphereCollider = trajectoryCollider as SphereCollider; var sphereCollider = m_GameObject.AddComponent <SphereCollider>(); sphereCollider.center = trajectorySphereCollider.center; sphereCollider.radius = trajectorySphereCollider.radius; sphereCollider.enabled = false; } else if (trajectoryCollider is CapsuleCollider) { var trajectoryCapsuleCollider = trajectoryCollider as CapsuleCollider; var capsuleCollider = m_GameObject.AddComponent <CapsuleCollider>(); capsuleCollider.center = trajectoryCapsuleCollider.center; capsuleCollider.radius = trajectoryCapsuleCollider.radius; capsuleCollider.height = trajectoryCapsuleCollider.height; capsuleCollider.direction = trajectoryCapsuleCollider.direction; capsuleCollider.enabled = false; } else { Debug.LogError($"Error: The collider of type {trajectoryCollider.GetType()} is not supported on the trajectory object " + m_ThrownObject.name); } m_GameObject.layer = LayerManager.SubCharacter; } ObjectPoolBase.Destroy(instantiatedThrownObject); } m_ThrowableItemPerpectiveProperties = m_ActivePerspectiveProperties as IThrowableItemPerspectiveProperties; if (m_ShowTrajectoryOnAim && m_TrajectoryObject == null) { Debug.LogError($"Error: A TrajectoryObject must be added to the {m_GameObject.name} GameObject in order for the trajectory to be shown."); } if (m_ThrownObject == null) { Debug.LogError($"Error: A ThrownObject must be assigned to the {m_GameObject.name} GameObject."); } EventHandler.RegisterEvent <bool, bool>(m_Character, "OnAimAbilityStart", OnAim); EventHandler.RegisterEvent(m_Character, "OnAnimatorReequipThrowableItem", ReequipThrowableItem); }
/// <summary> /// Move and rotate the object according to a parabolic trajectory. /// </summary> protected override void FixedUpdate() { base.FixedUpdate(); if (Time.time > m_RemoveTime) // The shell should be removed. { m_Transform.localScale = Vector3.Lerp(m_Transform.localScale, Vector3.zero, TimeUtility.FramerateDeltaTime * 0.2f); if (Time.time > m_RemoveTime + 0.5f) { ObjectPoolBase.Destroy(m_GameObject); } } }
/// <summary> /// Destroys the object. /// </summary> /// <param name="hitPosition">The position of the destruction.</param> /// <param name="hitNormal">The normal direction of the destruction.</param> public void Destruct(Vector3 hitPosition, Vector3 hitNormal) { for (int i = 0; i < m_SpawnedObjectsOnDestruction.Length; ++i) { if (m_SpawnedObjectsOnDestruction[i] == null) { continue; } var spawnedObject = m_SpawnedObjectsOnDestruction[i].Instantiate(hitPosition, hitNormal, m_NormalizedGravity); if (spawnedObject == null) { continue; } var explosion = spawnedObject.GetCachedComponent <Explosion>(); if (explosion != null) { explosion.Explode(m_DamageAmount, m_ImpactForce, m_ImpactForceFrames, m_Originator); } } // The component and collider no longer need to be enabled after the object has been destroyed. if (m_Collider != null) { m_Collider.enabled = false; } if (m_ParticleSystem != null) { m_ParticleSystem.Stop(); } m_Destroyed = true; m_DestroyEvent = null; enabled = false; // The destructible should be destroyed. #if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER if (NetworkObjectPool.IsNetworkActive()) { // The object may have already been destroyed over the network. if (!m_GameObject.activeSelf) { return; } NetworkObjectPool.Destroy(m_GameObject); return; } #endif ObjectPoolBase.Destroy(m_GameObject); }
/// <summary> /// The object has been picked up. /// </summary> /// <param name="pickedUpBy">A reference to the object that picked up the object.</param> protected virtual void ObjectPickedUp(GameObject pickedUpBy) { // The object may not have been instantiated within the scene. if (m_GameObject == null) { return; } m_IsDepleted = true; // Send an event notifying of the pickup. EventHandler.ExecuteEvent(pickedUpBy, "OnObjectPickedUp", this); // Optionally play a pickup sound if the object picking up the item is attached to a camera. // A null GameObject indicates that the clip will play from the AudioManager. var foundCamera = Shared.Camera.CameraUtility.FindCamera(pickedUpBy); if (foundCamera != null) { m_PickupAudioClipSet.PlayAtPosition(m_Transform.position); } if (ObjectPoolBase.InstantiatedWithPool(m_GameObject)) { #if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER if (NetworkObjectPool.IsNetworkActive()) { NetworkObjectPool.Destroy(m_GameObject); return; } #endif ObjectPoolBase.Destroy(m_GameObject); } else { // Deactivate the pickup for now. It can appear again if a Respawner component is attached to the GameObject. m_GameObject.SetActive(false); } }
/// <summary> /// Stops the cast. /// </summary> public override void Stop() { if (m_SpawnedObject != null) { #if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER if (NetworkObjectPool.IsNetworkActive()) { // The object may have already been destroyed over the network. if (!m_GameObject.activeSelf) { return; } NetworkObjectPool.Destroy(m_SpawnedObject); return; } #endif ObjectPoolBase.Destroy(m_SpawnedObject); m_SpawnedObject = null; } base.Stop(); }
/// <summary> /// Instantiates a new decal. /// </summary> /// <param name="original">The original prefab to spawn an instance of.</param> /// <param name="hit">The RaycastHit which caused the footprint to spawn.</param> /// <param name="rotation">The rotation of the decal which should be spawned.</param> /// <param name="scale">The scale of the decal to spawn.</param> /// <param name="allowedEdgeOverlap">How close to the edge the footprint is allowed to spawn.</param> /// <returns>The spawned decal. Can be null.</returns> private GameObject SpawnDecal(GameObject original, RaycastHit hit, Quaternion rotation, float scale, float allowedEdgeOverlap) { // Prevent z fighting by slightly raising the decal off of the surface. var decal = ObjectPoolBase.Instantiate(original, hit.point + (hit.normal * 0.001f), rotation); // Only set the decal parent to the hit transform on uniform objects to prevent stretching. if (MathUtility.IsUniform(hit.transform.localScale)) { decal.transform.parent = hit.transform; } if (scale != 1) { var vectorScale = Vector3.one; vectorScale.x = vectorScale.y = scale; decal.transform.localScale = vectorScale; } // Destroy the object if it cannot be cached. The object won't be able to be cached if it doesn't have all of the required components. if (!CacheMeshAndRenderer(decal)) { ObjectPoolBase.Destroy(decal); return(null); } // Do a test on the decal's quad to ensure all four corners are flush against a surface. This will prevent the decal from sticking out on an edge. if (allowedEdgeOverlap < 0.5f) { if (!DoQuadTest(decal, allowedEdgeOverlap)) { ObjectPoolBase.Destroy(decal); return(null); } } // The decal can be added. Add(decal); return(decal); }
/// <summary> /// Returns the GameObject back to the ObjectPool. /// </summary> private void PoolGameObject() { // The particle may be looping so it shouldn't be stopped yet. if (m_ParticleSystem.IsAlive(true)) { m_PoolEvent = SchedulerBase.Schedule(m_ParticleSystem.main.duration, PoolGameObject); return; } #if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER if (NetworkObjectPool.IsNetworkActive()) { // The object may have already been destroyed over the network. if (!m_GameObject.activeSelf) { return; } NetworkObjectPool.Destroy(m_GameObject); return; } #endif m_PoolEvent = null; ObjectPoolBase.Destroy(m_GameObject); }
/// <summary> /// Place itself back in the ObjectPool. /// </summary> public void DestroySelf() { ObjectPoolBase.Destroy(m_GameObject); }
/// <summary> /// Removes any slices which have existed for more than the visible time. /// </summary> private void RemoveOldSlices() { if (m_TrailSlicesCount == 0) { if (!m_GenerateSlices) { if (ObjectPoolBase.InstantiatedWithPool(m_GameObject)) { ObjectPoolBase.Destroy(m_GameObject); } else { m_GameObject.SetActive(false); } } return; } var startIndex = m_TrailSlicesIndex - m_TrailSlicesCount + 1; if (startIndex < 0) { startIndex = m_TrailSlices.Length + startIndex; } var count = m_TrailSlicesCount; for (int i = 0; i < count; ++i) { var trailSlice = m_TrailSlices[(startIndex + i) % m_TrailSlices.Length]; if (trailSlice.Time + m_VisibilityTime > Time.time) { break; } // The slice has existed for more than the visiblity time - remove it by decreasing the count. m_TrailSlicesCount--; } if (m_SmoothedTrailSlicesCount == 0) { return; } startIndex = m_SmoothedTrailSlicesIndex - m_SmoothedTrailSlicesCount + 1; if (startIndex < 0) { startIndex = m_SmoothedTrailSlices.Length + startIndex; } count = m_SmoothedTrailSlicesCount; for (int i = 0; i < count; ++i) { var trailSlice = m_SmoothedTrailSlices[(startIndex + i) % m_SmoothedTrailSlices.Length]; if (trailSlice.Time + m_VisibilityTime > Time.time) { break; } // The slice has existed for more than the visiblity time - remove it by decreasing the count. m_SmoothedTrailSlicesCount--; m_SmoothedTrailSlicesPrevCount--; } if (m_TrailSlicesCount == 0 && m_SmoothedTrailSlicesCount == 0) { m_GenerateSlices = false; } }
/// <summary> /// Places the object back in the ObjectPool. /// </summary> private void DestroyObject() { ObjectPoolBase.Destroy(gameObject); }
/// <summary> /// Remove the object. /// </summary> private void Remove() { ObjectPoolBase.Destroy(m_GameObject); m_RemoveEvent = null; }
/// <summary> /// Throws the throwable object. /// </summary> public void ThrowItem() { if (m_Thrown || m_InstantiatedThrownObject == null) { return; } m_Thrown = true; #if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER // The object has been thrown. If the ItemAction is on the server then that object should be spawned on the network. // Non-server actions should disable the mesh renderers so the object can take its place. The mesh renderers will be enabled again in a separate call. if (m_NetworkInfo != null) { EnableObjectMeshRenderers(false); if (!m_NetworkInfo.IsServer()) { if (m_InstantiatedThrownObject != null) { ObjectPoolBase.Destroy(m_InstantiatedThrownObject); m_InstantiatedThrownObject = null; } return; } } #endif m_InstantiatedThrownObject.transform.parent = null; // The collider was previously disabled. Enable it again when it is thrown. var thrownCollider = m_InstantiatedThrownObject.GetCachedComponent <Collider>(); thrownCollider.enabled = true; // When the item is used the trajectory object should start moving on its own. // The throwable item may be on the other side of an object (especially in the case of separate arms for the first person perspective). Perform a linecast // to ensure the throwable item doesn't move through any objects. var collisionEnabled = m_CharacterLocomotion.CollisionLayerEnabled; m_CharacterLocomotion.EnableColliderCollisionLayer(false); if (!m_CharacterLocomotion.ActiveMovementType.UseIndependentLook(false) && Physics.Linecast(m_CharacterLocomotion.LookSource.LookPosition(true), m_InstantiatedTrajectoryObject.transform.position, out m_RaycastHit, m_ImpactLayers, QueryTriggerInteraction.Ignore)) { m_InstantiatedTrajectoryObject.transform.position = m_RaycastHit.point; } m_CharacterLocomotion.EnableColliderCollisionLayer(collisionEnabled); var trajectoryTransform = m_ThrowableItemPerpectiveProperties.TrajectoryLocation != null ? m_ThrowableItemPerpectiveProperties.TrajectoryLocation : m_CharacterTransform; var lookDirection = m_LookSource.LookDirection(trajectoryTransform.TransformPoint(m_TrajectoryOffset), false, m_ImpactLayers, true, true); #if ULTIMATE_CHARACTER_CONTROLLER_VR if (m_VRThrowableItem != null && m_CharacterLocomotion.FirstPersonPerspective) { m_Velocity = m_VRThrowableItem.GetVelocity(); } #endif var velocity = MathUtility.TransformDirection(m_Velocity, Quaternion.LookRotation(lookDirection, m_CharacterLocomotion.Up)); // Prevent the item from being thrown behind the character. This can happen if the character is looking straight up and there is a positive // y velocity. Gravity will cause the thrown object to go in the opposite direction. if (Vector3.Dot(velocity.normalized, m_CharacterTransform.forward) < 0 && m_CharacterTransform.InverseTransformDirection(velocity.normalized).y > 0) { velocity = m_CharacterTransform.up * velocity.magnitude; } m_InstantiatedTrajectoryObject.Initialize(m_CharacterLocomotion.Alive ? (velocity + (m_CharacterTransform.forward * m_CharacterLocomotion.LocalVelocity.z)) : Vector3.zero, Vector3.zero, m_Character, false); #if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER if (m_NetworkInfo != null) { NetworkObjectPool.NetworkSpawn(m_ThrownObject, m_InstantiatedThrownObject, true); } #endif // Optionally change the layer after the object has been thrown. This allows the object to change from the first person Overlay layer // to the Default layer after it has cleared the character's hands. if (m_StartLayer != m_ThrownLayer) { SchedulerBase.ScheduleFixed(m_LayerChangeDelay, ChangeThrownLayer, m_InstantiatedThrownObject); } }
/// <summary> /// Place the object back in the ObjectPool. /// </summary> private void Destroy() { ObjectPoolBase.Destroy(gameObject); m_DestructionEvent = null; }
/// <summary> /// The object is no longer alive. /// </summary> /// <param name="position">The position of the damage.</param> /// <param name="force">The amount of force applied to the object while taking the damage.</param> /// <param name="attacker">The GameObject that killed the character.</param> public virtual void Die(Vector3 position, Vector3 force, GameObject attacker) { #if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER if (m_NetworkInfo != null && m_NetworkInfo.IsLocalPlayer()) { m_NetworkHealthMonitor.Die(position, force, attacker); } #endif // Spawn any objects on death, such as an explosion if the object is an explosive barrel. if (m_SpawnedObjectsOnDeath != null) { for (int i = 0; i < m_SpawnedObjectsOnDeath.Length; ++i) { var spawnedObject = ObjectPoolBase.Instantiate(m_SpawnedObjectsOnDeath[i], m_Transform.position, m_Transform.rotation); Explosion explosion; if ((explosion = spawnedObject.GetCachedComponent <Explosion>()) != null) { explosion.Explode(gameObject); } var rigidbodies = spawnedObject.GetComponentsInChildren <Rigidbody>(); for (int j = 0; j < rigidbodies.Length; ++j) { rigidbodies[j].AddForceAtPosition(force, position); } } } // Destroy any objects on death. The objects will be placed back in the object pool if they were created within it otherwise the object will be destroyed. if (m_DestroyedObjectsOnDeath != null) { for (int i = 0; i < m_DestroyedObjectsOnDeath.Length; ++i) { if (ObjectPoolBase.InstantiatedWithPool(m_DestroyedObjectsOnDeath[i])) { ObjectPoolBase.Destroy(m_DestroyedObjectsOnDeath[i]); } else { Object.Destroy(m_DestroyedObjectsOnDeath[i]); } } } // Change the layer to a death layer. if (m_DeathLayer.value != 0) { m_AliveLayer = m_GameObject.layer; m_GameObject.layer = m_DeathLayer; } // Play any take death audio. Use PlayAtPosition because the audio won't play if the GameObject is inactive. m_DeathAudioClipSet.PlayAtPosition(m_Transform.position); // Deactivate the object if requested. if (m_DeactivateOnDeath) { SchedulerBase.Schedule(m_DeactivateOnDeathDelay, Deactivate); } // The attributes shouldn't regenerate. if (m_ShieldAttribute != null) { m_ShieldAttribute.CancelAutoUpdate(); } if (m_HealthAttribute != null) { m_HealthAttribute.CancelAutoUpdate(); } // Notify those interested. EventHandler.ExecuteEvent <Vector3, Vector3, GameObject>(m_GameObject, "OnDeath", position, force, attacker); if (m_OnDeathEvent != null) { m_OnDeathEvent.Invoke(position, force, attacker); } }