Untitled

 avatar
unknown
plain_text
2 months ago
10 kB
8
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