Untitled
unknown
plain_text
2 months ago
10 kB
7
Indexable
using UnityEngine;
using UnityEngine.AI;
using TMPro;
public class EnemyAttackControllerWithHealth : MonoBehaviour
{
private enum EnemyState
{
Idle,
Patrol,
Chase,
Attack,
Hide
}
private EnemyState state;
private EnemyState previousState;
private NavMeshAgent agent;
private Transform player;
private Animator animator;
private EnemyHitCounter hitCounter;
[Header("Animator Controllers")]
public RuntimeAnimatorController idleController;
public RuntimeAnimatorController walkController;
public RuntimeAnimatorController attackController;
public RuntimeAnimatorController linkController;
[Header("Detection")]
public float detectionRadius = 10f;
public float attackDistance = 2f;
[Header("Hide Settings")]
public int hitsBeforeHide = 3;
private bool isHiding = false;
private bool canHide = true;
// HIDE LOGIKA
private Vector3 hideTarget;
private bool hasHideTarget = false;
private bool reachedHidePoint = false;
private float waitTimer = 0f;
public float waitDuration = 3f;
[Header("Patrol")]
public float patrolRadius = 10f;
private Vector3 patrolPoint;
[Header("Attack")]
public float damageAmount = 10f;
public float damageInterval = 2f;
private float damageTimer;
[Header("Cornering Speed")]
public float maxSpeed = 6f;
public float minSpeed = 2f;
public int lookAheadPoints = 2;
[Header("Enrage")]
public float enrageSpeedMultiplier = 1.5f;
public float enrageDamageMultiplier = 0.5f;
private bool isEnraged = false;
[Header("Area Settings")]
public string slowAreaName = "SlowArea";
public float slowAreaSpeed = 2f;
public float normalAreaCost = 1f;
public float slowAreaHighCost = 1000f;
[Header("Health")]
public TMP_Text healthText;
public float maxHealth = 100f;
private float currentHealth;
private bool isGameOver = false;
void Start()
{
agent = GetComponent<NavMeshAgent>();
animator = GetComponent<Animator>();
hitCounter = GetComponent<EnemyHitCounter>();
player = GameObject.FindGameObjectWithTag("Player")?.transform;
currentHealth = maxHealth;
// START su Idle
state = EnemyState.Idle;
previousState = state;
patrolPoint = GetRandomPoint();
if (idleController != null)
animator.runtimeAnimatorController = idleController;
int slowAreaIndex = NavMesh.GetAreaFromName(slowAreaName);
agent.SetAreaCost(slowAreaIndex, slowAreaHighCost);
agent.stoppingDistance = attackDistance * 0.8f;
}
void Update()
{
if (hitCounter != null && hitCounter.isDead) return;
if (player == null || isGameOver) return;
UpdateFSM();
if (state != previousState)
{
if (previousState == EnemyState.Attack) damageTimer = 0;
if (state == EnemyState.Patrol) patrolPoint = GetRandomPoint();
}
ExecuteState();
previousState = state;
HandleCorneringSpeed();
HandleAreaCosts();
}
void UpdateFSM()
{
float dist = Vector3.Distance(transform.position, player.position);
// HIDE PRIORITY
if (isHiding || (canHide && hitCounter != null && hitCounter.CurrentHits >= hitsBeforeHide))
{
if (!isHiding)
{
isHiding = true;
}
state = EnemyState.Hide;
return;
}
if (dist <= attackDistance)
state = EnemyState.Attack;
else if (dist <= 15f)
state = EnemyState.Chase;
else if (dist <= 25f)
state = EnemyState.Patrol;
else
state = EnemyState.Idle;
}
void ExecuteState()
{
switch (state)
{
case EnemyState.Idle: Idle(); break;
case EnemyState.Patrol: Patrol(); break;
case EnemyState.Chase: Chase(); break;
case EnemyState.Attack: Attack(); break;
case EnemyState.Hide: Hide(); break;
}
}
void Idle()
{
agent.isStopped = true;
if (animator.runtimeAnimatorController != idleController)
animator.runtimeAnimatorController = idleController;
}
void Patrol()
{
agent.isStopped = false;
if (Vector3.Distance(transform.position, patrolPoint) < 1f)
patrolPoint = GetRandomPoint();
agent.SetDestination(patrolPoint);
if (animator.runtimeAnimatorController != walkController)
animator.runtimeAnimatorController = walkController;
}
void Chase()
{
agent.isStopped = false;
agent.SetDestination(player.position);
if (animator.runtimeAnimatorController != walkController)
animator.runtimeAnimatorController = walkController;
}
void Attack()
{
float dist = Vector3.Distance(transform.position, player.position);
if (dist > attackDistance * 1.5f)
{
state = EnemyState.Chase;
return;
}
agent.isStopped = true;
agent.ResetPath();
Vector3 dir = (player.position - transform.position).normalized;
dir.y = 0;
if (dir != Vector3.zero)
{
Quaternion rot = Quaternion.LookRotation(dir);
transform.rotation = Quaternion.Slerp(transform.rotation, rot, Time.deltaTime * 10f);
}
if (animator.runtimeAnimatorController != attackController)
animator.runtimeAnimatorController = attackController;
damageTimer += Time.deltaTime;
if (damageTimer >= damageInterval)
{
damageTimer = 0f;
TakeDamage(damageAmount);
}
}
void Hide()
{
// 1. GAUNAM RANDOM TAŠKĄ
if (!hasHideTarget)
{
hideTarget = GetRandomPoint();
agent.SetDestination(hideTarget);
hasHideTarget = true;
reachedHidePoint = false;
}
// 2. JUDAM
if (!reachedHidePoint)
{
agent.isStopped = false;
if (animator.runtimeAnimatorController != walkController)
animator.runtimeAnimatorController = walkController;
if (!agent.pathPending && agent.remainingDistance <= 0.5f)
{
reachedHidePoint = true;
agent.isStopped = true;
waitTimer = 0f;
}
return;
}
// 3. STOVIM (IDLE)
agent.isStopped = true;
if (animator.runtimeAnimatorController != idleController)
animator.runtimeAnimatorController = idleController;
waitTimer += Time.deltaTime;
// 4. GRĮŽTAM PAS PLAYER
if (waitTimer >= waitDuration)
{
EndHide();
}
}
void EndHide()
{
isHiding = false;
canHide = false;
hasHideTarget = false;
reachedHidePoint = false;
waitTimer = 0f;
if (hitCounter != null) hitCounter.ResetHits();
state = EnemyState.Chase;
}
Vector3 GetRandomPoint()
{
Vector3 random = Random.insideUnitSphere * patrolRadius;
random += transform.position;
NavMeshHit hit;
NavMesh.SamplePosition(random, out hit, patrolRadius, NavMesh.AllAreas);
return hit.position;
}
void HandleAreaCosts()
{
int slowAreaIndex = NavMesh.GetAreaFromName(slowAreaName);
NavMeshHit hit;
if (NavMesh.SamplePosition(transform.position, out hit, 1f, NavMesh.AllAreas))
{
int area = hit.mask;
if (area == slowAreaIndex)
{
agent.SetAreaCost(slowAreaIndex, slowAreaHighCost);
agent.speed = slowAreaSpeed;
}
else
{
agent.SetAreaCost(slowAreaIndex, normalAreaCost);
}
}
}
void HandleCorneringSpeed()
{
if (agent.path == null || agent.path.corners.Length < 3) return;
int currentIndex = 0;
float minDist = Mathf.Infinity;
for (int i = 0; i < agent.path.corners.Length; i++)
{
float dist = Vector3.Distance(transform.position, agent.path.corners[i]);
if (dist < minDist)
{
minDist = dist;
currentIndex = i;
}
}
int lookAheadIndex = Mathf.Min(currentIndex + lookAheadPoints, agent.path.corners.Length - 1);
Vector3 A = agent.path.corners[currentIndex];
Vector3 B = agent.path.corners[Mathf.Min(currentIndex + 1, agent.path.corners.Length - 1)];
Vector3 C = agent.path.corners[lookAheadIndex];
float angle = Vector3.Angle(B - A, C - B);
float baseSpeed = isEnraged ? maxSpeed * enrageSpeedMultiplier : maxSpeed;
float minSpd = isEnraged ? minSpeed * enrageSpeedMultiplier : minSpeed;
agent.speed = Mathf.Lerp(baseSpeed, minSpd, angle / 180f);
}
public void Enrage()
{
if (isEnraged) return;
isEnraged = true;
damageInterval *= enrageDamageMultiplier;
}
void TakeDamage(float amount)
{
currentHealth -= amount;
currentHealth = Mathf.Clamp(currentHealth, 0, maxHealth);
if (healthText != null)
healthText.text = $"Health: {currentHealth:F0}%";
if (currentHealth <= 0)
isGameOver = true;
}
}Editor is loading...
Leave a Comment