Space-Smash-Out/Assets/FORGE3D/Sci-Fi Effects/Code/F3DProjectile.cs

220 lines
6.1 KiB
C#

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<Transform>();
particles = GetComponentsInChildren<ParticleSystem>();
}
// 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;
}
/// <summary>
/// Checks if a HitDetector was hit and registers a hit
/// on it if so.
/// </summary>
/// <param name="hitPoint"></param>
/// <returns>OwnerId of the hit detector (-1 on none)</returns>
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;
}
}
}