Started working on turrets
This commit is contained in:
202
Assets/Scripts/Turret.cs
Normal file
202
Assets/Scripts/Turret.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user