initial commit
This commit is contained in:
166
Assets/Scripts/Projectile.cs
Normal file
166
Assets/Scripts/Projectile.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user