Files

202 lines
5.3 KiB
C#

using System.Collections;
using UnityEngine;
public class Turret : MonoBehaviour
{
[SerializeField] Transform turretBase; // horizontal rotation
[SerializeField] Transform cannon; // vertical rotation
[SerializeField] Transform[] barrels;
[SerializeField] float rotationSpeed = 5f;
[SerializeField] float turretRange = 10f;
[SerializeField] float fireRate = 1f;
[SerializeField] int damage = 10;
[SerializeField] bool isEnabled = true;
[SerializeField] float smoothTime = 0.2f;
[SerializeField] float projectileSpeed = 30f;
[SerializeField] GameObject hitVFXPrefab;
private Quaternion baseStartRot;
private Quaternion cannonStartRot;
float baseYawVelocity;
float cannonPitchVelocity;
Transform target;
float fireCooldown;
void Awake()
{
baseStartRot = turretBase.localRotation;
cannonStartRot = cannon.localRotation;
}
void Update()
{
if (!isEnabled) return;
FindTarget();
if (target == null)
{
ResetTurret();
return;
}
RotateTurret();
HandleShoot();
}
void FindTarget()
{
Collider[] hits = Physics.OverlapSphere(transform.position, turretRange);
float closestDist = Mathf.Infinity;
Transform closest = null;
foreach (var hit in hits)
{
EnemyHealth enemy = hit.GetComponent<EnemyHealth>();
if (enemy == null) continue;
float dist = Vector3.Distance(transform.position, hit.transform.position);
if (dist < closestDist)
{
closestDist = dist;
closest = hit.transform;
}
}
target = closest;
}
void RotateTurret()
{
Vector3 predicted = PredictTargetPosition();
Vector3 dir = predicted - turretBase.position;
// Horizontal rotation
Vector3 flatDir = new Vector3(dir.x, 0f, dir.z);
float targetYaw = Quaternion.LookRotation(flatDir).eulerAngles.y;
float newYaw = Mathf.SmoothDampAngle(
turretBase.eulerAngles.y,
targetYaw,
ref baseYawVelocity,
smoothTime
);
turretBase.rotation = Quaternion.Euler(0f, newYaw, 0f);
// Vertical rotation
float targetPitch = Quaternion.LookRotation(dir).eulerAngles.x;
float newPitch = Mathf.SmoothDampAngle(
cannon.localEulerAngles.x,
targetPitch,
ref cannonPitchVelocity,
smoothTime
);
cannon.localRotation = Quaternion.Euler(newPitch, 0f, 0f);
}
void HandleShoot()
{
fireCooldown -= Time.deltaTime;
if (fireCooldown > 0f) return;
fireCooldown = 1f / fireRate;
// 1. Play muzzle flashes on all barrels
foreach (var barrel in barrels)
{
var flash = barrel.GetComponentInChildren<ParticleSystem>();
flash?.Play();
StartCoroutine(BarrelRecoil(barrel));
}
// 3. Raycast from cannon forward (or predicted aim point)
RaycastHit hit;
Vector3 fireOrigin = cannon.position;
Vector3 predicted = PredictTargetPosition();
Vector3 fireDirection = (predicted - fireOrigin).normalized;
if (Physics.Raycast(fireOrigin, fireDirection, out hit, turretRange))
{
// 4. Spawn hit VFX
if (hitVFXPrefab != null)
Instantiate(hitVFXPrefab, hit.point, Quaternion.identity);
// 5. Apply damage
EnemyHealth enemyHealth = hit.transform.GetComponent<EnemyHealth>();
enemyHealth?.TakeDamage(damage);
}
}
public void Activate()
{
isEnabled = true;
}
public void Deactivate()
{
isEnabled = false;
target = null;
}
Vector3 PredictTargetPosition()
{
Rigidbody rb = target.GetComponent<Rigidbody>();
if (rb == null) return target.position;
Vector3 targetPos = target.position;
Vector3 targetVel = rb.linearVelocity;
Vector3 dir = targetPos - cannon.position;
float distance = dir.magnitude;
float timeToHit = distance / projectileSpeed;
return targetPos + targetVel * timeToHit;
}
void ResetTurret()
{
// Smoothly return to idle rotation
float smooth = rotationSpeed / 5 * Time.deltaTime;
turretBase.localRotation = Quaternion.Lerp(
turretBase.localRotation,
baseStartRot,
smooth
);
cannon.localRotation = Quaternion.Lerp(
cannon.localRotation,
cannonStartRot,
smooth
);
}
IEnumerator BarrelRecoil(Transform barrel)
{
Vector3 startPos = barrel.localPosition;
Vector3 recoilPos = startPos + new Vector3(0f, 0f, -0.2f); // adjust recoil distance
float t = 0f;
float recoilTime = 0.05f;
// Move backward
while (t < 1f)
{
t += Time.deltaTime / recoilTime;
barrel.localPosition = Vector3.Lerp(startPos, recoilPos, t);
yield return null;
}
// Move forward
t = 0f;
while (t < 1f)
{
t += Time.deltaTime / recoilTime;
barrel.localPosition = Vector3.Lerp(recoilPos, startPos, t);
yield return null;
}
}
}