using UnityEngine; using System.Collections; namespace FORGE3D { public class F3DProjectile : MonoBehaviour { public WeaponEffect fxType; // Weapon type public LayerMask layerMask; [Tooltip("The amount of damage this projectil inflicts generally.")] public float Damage = 10f; [Tooltip("The magnitude of the blast when hitting something.")] public float BlastMagnitude = 1f; [HideInInspector] public int OwnerId = 0; public float LifeTime = 5f; // Projectile life time public float despawnDelay; // Delay despawn in ms public float velocity = 300f; // Projectile velocity public float RaycastAdvance = 2f; // Raycast advance multiplier public bool DelayDespawn = false; // Projectile despawn flag public ParticleSystem[] delayedParticles; // Array of delayed particles ParticleSystem[] particles; // Array of projectile particles new Transform transform; // Cached transform RaycastHit hitPoint; // Raycast structure bool isHit = false; // Projectile hit flag bool isFXSpawned = false; // Hit FX prefab spawned flag float timer = 0f; // Projectile timer float fxOffset; // Offset of fxImpact void Awake() { // Cache transform and get all particle systems attached transform = GetComponent(); particles = GetComponentsInChildren(); } // OnSpawned called by pool manager public void OnSpawned() { // Reset flags and raycast structure isHit = false; isFXSpawned = false; timer = 0f; hitPoint = new RaycastHit(); } // OnDespawned called by pool manager public void OnDespawned() { } // Stop attached particle systems emission and allow them to fade out before despawning void Delay() { if (particles.Length > 0 && delayedParticles.Length > 0) { bool delayed; for (int i = 0; i < particles.Length; i++) { delayed = false; for (int y = 0; y < delayedParticles.Length; y++) if (particles[i] == delayedParticles[y]) { delayed = true; break; } particles[i].Stop(false); if (!delayed) particles[i].Clear(false); } } } // OnDespawned called by pool manager void OnProjectileDestroy() { F3DPoolManager.Pools["GeneratedPool"].Despawn(transform); } // TODO: for my purposes this is not enough control over this effect // I will have to implement HitDetectors for everything which can be hit void ApplyForce(float force) { // if (hitPoint.rigidbody != null) // hitPoint.rigidbody.AddForceAtPosition(transform.forward * force, hitPoint.point, // ForceMode.VelocityChange); } void Update() { // If something was hit if (isHit) { // Execute once if (!isFXSpawned) { // Invoke corresponding method that spawns FX switch (fxType) { case WeaponEffect.Vulcan: F3DFXController.instance.VulcanImpact(hitPoint.point + hitPoint.normal * fxOffset); ApplyForce(2.5f); break; case WeaponEffect.SoloGun: F3DFXController.instance.SoloGunImpact(hitPoint.point + hitPoint.normal * fxOffset); ApplyForce(25f); break; case WeaponEffect.Seeker: F3DFXController.instance.SeekerImpact(hitPoint.point + hitPoint.normal * fxOffset); ApplyForce(30f); break; case WeaponEffect.PlasmaGun: F3DFXController.instance.PlasmaGunImpact(hitPoint.point + hitPoint.normal * fxOffset); ApplyForce(25f); break; case WeaponEffect.LaserImpulse: F3DFXController.instance.LaserImpulseImpact(hitPoint.point + hitPoint.normal * fxOffset); ApplyForce(25f); break; } isFXSpawned = true; } // Despawn current projectile if (!DelayDespawn || (DelayDespawn && (timer >= despawnDelay))) OnProjectileDestroy(); } // No collision occurred yet else { // Projectile step per frame based on velocity and time Vector3 step = transform.forward * Time.deltaTime * velocity; // Raycast for targets with ray length based on frame step by ray cast advance multiplier if (Physics.Raycast(transform.position, transform.forward, out hitPoint, step.magnitude * RaycastAdvance, layerMask)) { if (DetectorCollision(hitPoint) == OwnerId) { transform.position += 2 * step; timer += Time.deltaTime; return; } isHit = true; // Invoke delay routine if required if (DelayDespawn) { // Reset projectile timer and let particles systems stop emitting and fade out correctly timer = 0f; Delay(); } } // Nothing hit else { // Projectile despawn after run out of time if (timer >= LifeTime) OnProjectileDestroy(); } // Advances projectile forward transform.position += step; } // Updates projectile timer timer += Time.deltaTime; } /// /// Checks if a HitDetector was hit and registers a hit /// on it if so. /// /// /// OwnerId of the hit detector (-1 on none) int DetectorCollision(RaycastHit hitPoint) { if (hitPoint.collider.gameObject.TryGetComponent(out HitDetection detector)) { // Check if the one firing was hit if (detector.OwnerId == OwnerId) { return OwnerId; } ProjectileDamage inflictling = new() { DamageValue = Damage, ImpactMagnitude = BlastMagnitude, ImpactPoint = hitPoint.point, ImpactDirection = transform.forward }; detector.RegisterHit(inflictling); return detector.OwnerId; } return -1; } //Set offset public void SetOffset(float offset) { fxOffset = offset; } } }