initial commit
This commit is contained in:
222
Assets/Scripts/EnemyController.cs
Normal file
222
Assets/Scripts/EnemyController.cs
Normal file
@@ -0,0 +1,222 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
[RequireComponent(typeof(NavMeshAgent))]
|
||||
public class EnemyController : MonoBehaviour
|
||||
{
|
||||
public enum State { Idle, Chase, Attack, Dead }
|
||||
private State currentState = State.Idle;
|
||||
|
||||
[Header("Stats")]
|
||||
[SerializeField] private int maxHealth = 60;
|
||||
[SerializeField] private int attackDamage = 15;
|
||||
[SerializeField] private float attackRange = 1.8f;
|
||||
[SerializeField] private float attackCooldown = 1.2f;
|
||||
[SerializeField] private float detectionRadius = 18f;
|
||||
|
||||
[Header("Ranged Attack Settings (Optional)")]
|
||||
[SerializeField] private bool isRangedEnemy = false;
|
||||
[SerializeField] private GameObject rangedProjectilePrefab;
|
||||
[SerializeField] private Transform projectileSpawnPoint;
|
||||
[SerializeField] private float projectileSpeed = 25f;
|
||||
|
||||
[Header("References")]
|
||||
[SerializeField] private Animator animator;
|
||||
[SerializeField] private AudioSource audioSource;
|
||||
[SerializeField] private AudioClip attackSound;
|
||||
[SerializeField] private AudioClip hitSound;
|
||||
[SerializeField] private AudioClip deathSound;
|
||||
|
||||
private NavMeshAgent agent;
|
||||
private Transform playerTransform;
|
||||
private int currentHealth;
|
||||
private float nextAttackTime;
|
||||
|
||||
public State CurrentState => currentState;
|
||||
public int CurrentHealth => currentHealth;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
agent = GetComponent<NavMeshAgent>();
|
||||
if (animator == null)
|
||||
{
|
||||
animator = GetComponentInChildren<Animator>();
|
||||
}
|
||||
if (audioSource == null)
|
||||
{
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
}
|
||||
currentHealth = maxHealth;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
GameObject playerObj = GameObject.FindGameObjectWithTag("Player");
|
||||
if (playerObj != null)
|
||||
{
|
||||
playerTransform = playerObj.transform;
|
||||
}
|
||||
|
||||
AreaClearanceManager.Instance?.RegisterEnemy(this);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (currentState == State.Dead) return;
|
||||
|
||||
// Re-acquire player reference if it was spawned dynamically
|
||||
if (playerTransform == null)
|
||||
{
|
||||
GameObject playerObj = GameObject.FindGameObjectWithTag("Player");
|
||||
if (playerObj != null)
|
||||
{
|
||||
playerTransform = playerObj.transform;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float distanceToPlayer = Vector3.Distance(transform.position, playerTransform.position);
|
||||
|
||||
switch (currentState)
|
||||
{
|
||||
case State.Idle:
|
||||
if (distanceToPlayer <= detectionRadius)
|
||||
{
|
||||
currentState = State.Chase;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.Chase:
|
||||
if (agent.isOnNavMesh)
|
||||
{
|
||||
agent.SetDestination(playerTransform.position);
|
||||
}
|
||||
|
||||
if (animator != null)
|
||||
{
|
||||
animator.SetFloat("Speed", agent.velocity.magnitude);
|
||||
}
|
||||
|
||||
if (distanceToPlayer <= attackRange)
|
||||
{
|
||||
currentState = State.Attack;
|
||||
if (agent.isOnNavMesh)
|
||||
{
|
||||
agent.ResetPath();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case State.Attack:
|
||||
Vector3 lookTarget = new Vector3(playerTransform.position.x, transform.position.y, playerTransform.position.z);
|
||||
transform.LookAt(lookTarget);
|
||||
|
||||
if (Time.time >= nextAttackTime)
|
||||
{
|
||||
PerformAttack();
|
||||
nextAttackTime = Time.time + attackCooldown;
|
||||
}
|
||||
|
||||
if (distanceToPlayer > attackRange)
|
||||
{
|
||||
currentState = State.Chase;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void PerformAttack()
|
||||
{
|
||||
if (animator != null)
|
||||
{
|
||||
animator.SetTrigger("Attack");
|
||||
}
|
||||
|
||||
if (attackSound != null && audioSource != null)
|
||||
{
|
||||
audioSource.PlayOneShot(attackSound);
|
||||
}
|
||||
|
||||
if (isRangedEnemy && rangedProjectilePrefab != null)
|
||||
{
|
||||
// Fire projectile towards player
|
||||
Vector3 spawnPos = projectileSpawnPoint != null ? projectileSpawnPoint.position : transform.position + Vector3.up * 1.2f + transform.forward * 0.5f;
|
||||
Vector3 aimDir = (playerTransform.position + Vector3.up * 1.0f - spawnPos).normalized;
|
||||
GameObject projObj = Instantiate(rangedProjectilePrefab, spawnPos, Quaternion.LookRotation(aimDir));
|
||||
if (projObj.TryGetComponent<Projectile>(out var proj))
|
||||
{
|
||||
proj.Initialize(attackDamage, projectileSpeed, "Enemy");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Direct melee strike
|
||||
if (PlayerHealthController.Instance != null)
|
||||
{
|
||||
PlayerHealthController.Instance.TakeDamage(attackDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void TakeDamage(int damage)
|
||||
{
|
||||
if (currentState == State.Dead) return;
|
||||
|
||||
currentHealth -= damage;
|
||||
|
||||
if (hitSound != null && audioSource != null)
|
||||
{
|
||||
audioSource.PlayOneShot(hitSound);
|
||||
}
|
||||
|
||||
if (animator != null)
|
||||
{
|
||||
animator.SetTrigger("Hit");
|
||||
}
|
||||
|
||||
// Auto-aggro when damaged if idle
|
||||
if (currentState == State.Idle)
|
||||
{
|
||||
currentState = State.Chase;
|
||||
}
|
||||
|
||||
if (currentHealth <= 0)
|
||||
{
|
||||
Die();
|
||||
}
|
||||
}
|
||||
|
||||
private void Die()
|
||||
{
|
||||
currentState = State.Dead;
|
||||
|
||||
if (agent != null && agent.isOnNavMesh)
|
||||
{
|
||||
agent.isStopped = true;
|
||||
}
|
||||
if (agent != null)
|
||||
{
|
||||
agent.enabled = false;
|
||||
}
|
||||
|
||||
if (animator != null)
|
||||
{
|
||||
animator.SetBool("IsDead", true);
|
||||
}
|
||||
|
||||
if (deathSound != null && audioSource != null)
|
||||
{
|
||||
audioSource.PlayOneShot(deathSound);
|
||||
}
|
||||
|
||||
Collider col = GetComponent<Collider>();
|
||||
if (col != null)
|
||||
{
|
||||
col.enabled = false;
|
||||
}
|
||||
|
||||
AreaClearanceManager.Instance?.UnregisterEnemy(this);
|
||||
Destroy(gameObject, 5f);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user