Files
LowPolyFPS/Assets/Scripts/Projectile.cs

167 lines
5.1 KiB
C#
Raw Permalink Normal View History

2026-10-12 21:28:47 +01:00
using UnityEngine;
public class Projectile : MonoBehaviour
{
[Header("Flight Settings")]
[SerializeField] private float speed = 50f;
[SerializeField] private float lifeTime = 5f;
[SerializeField] private bool usePhysics = false;
[Header("Damage Settings")]
[SerializeField] private int damage = 50;
[SerializeField] private bool isExplosive = false;
[SerializeField] private float explosionRadius = 4f;
[SerializeField] private LayerMask damageableLayers = ~0;
[Header("Visual & Audio Effects")]
[SerializeField] private GameObject impactEffectPrefab;
[SerializeField] private AudioClip impactSound;
private Rigidbody rb;
private string ownerTag = "Player";
private bool hasImpacted = false;
private void Awake()
{
rb = GetComponent<Rigidbody>();
if (rb != null)
{
rb.useGravity = usePhysics;
if (usePhysics)
{
rb.linearVelocity = transform.forward * speed;
}
}
}
private void Start()
{
Destroy(gameObject, lifeTime);
}
private void Update()
{
if (!usePhysics || rb == null)
{
// Manual kinematic translation with raycast sweep to prevent tunneling
float step = speed * Time.deltaTime;
Ray ray = new Ray(transform.position, transform.forward);
if (Physics.Raycast(ray, out RaycastHit hit, step, damageableLayers, QueryTriggerInteraction.Ignore))
{
if (hit.collider.CompareTag(ownerTag)) return;
transform.position = hit.point;
OnHit(hit.collider, hit.point, hit.normal);
return;
}
transform.position += transform.forward * step;
}
}
public void Initialize(int customDamage, float customSpeed, string shooterTag)
{
damage = customDamage;
speed = customSpeed;
ownerTag = shooterTag;
if (rb != null && usePhysics)
{
rb.linearVelocity = transform.forward * speed;
}
}
private void OnTriggerEnter(Collider other)
{
if (hasImpacted || other.CompareTag(ownerTag)) return;
HandleCollision(other, transform.position, -transform.forward);
}
private void OnCollisionEnter(Collision collision)
{
if (hasImpacted || collision.collider.CompareTag(ownerTag)) return;
ContactPoint contact = collision.GetContact(0);
HandleCollision(collision.collider, contact.point, contact.normal);
}
private void OnHit(Collider hitCollider, Vector3 hitPoint, Vector3 hitNormal)
{
if (hasImpacted) return;
HandleCollision(hitCollider, hitPoint, hitNormal);
}
private void HandleCollision(Collider hitCollider, Vector3 hitPoint, Vector3 hitNormal)
{
hasImpacted = true;
if (isExplosive)
{
Explode(hitPoint);
}
else
{
// Direct hit damage
if (hitCollider.TryGetComponent<EnemyController>(out var enemy))
{
enemy.TakeDamage(damage);
}
else if (hitCollider.TryGetComponent<PlayerHealthController>(out var playerHealth))
{
playerHealth.TakeDamage(damage);
}
}
// Spawn visual impact effect
if (impactEffectPrefab != null)
{
Quaternion rot = hitNormal != Vector3.zero ? Quaternion.LookRotation(hitNormal) : Quaternion.identity;
GameObject impact = Instantiate(impactEffectPrefab, hitPoint, rot);
Destroy(impact, 2.5f);
}
// Play impact sound
if (impactSound != null)
{
AudioSource.PlayClipAtPoint(impactSound, hitPoint);
}
Destroy(gameObject);
}
private void Explode(Vector3 center)
{
Collider[] hits = Physics.OverlapSphere(center, explosionRadius, damageableLayers);
foreach (Collider col in hits)
{
if (col.CompareTag(ownerTag)) continue;
if (col.TryGetComponent<EnemyController>(out var enemy))
{
float distance = Vector3.Distance(center, col.transform.position);
float damageFalloff = Mathf.Clamp01(1f - (distance / explosionRadius));
int finalDamage = Mathf.RoundToInt(damage * damageFalloff);
enemy.TakeDamage(Mathf.Max(1, finalDamage));
}
else if (col.TryGetComponent<PlayerHealthController>(out var playerHealth))
{
float distance = Vector3.Distance(center, col.transform.position);
float damageFalloff = Mathf.Clamp01(1f - (distance / explosionRadius));
int finalDamage = Mathf.RoundToInt(damage * damageFalloff);
playerHealth.TakeDamage(Mathf.Max(1, finalDamage));
}
}
}
private void OnDrawGizmosSelected()
{
if (isExplosive)
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, explosionRadius);
}
}
}