0
\$\begingroup\$

I have a problem, I have a universal enemy script, which I use on all enemies, but as in the script when an enemy dies it is set to deactivate the current script and because of that the first enemy goes perfectly to kill him, but when I arrive at the second I can't attack him because he says the script is disabled and he can't access it, how can I do when I kill an enemy not to affect the main script of all enemies?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Enemy : MonoBehaviour
{ 
    #region Public Variables

    public int maxHealth = 100; //Enemy max health;
    public int currentHealth; // Enemy current health;

    public Transform leftLimit; //Left limit for patrolling;
    public Transform rightLimit; //Right limit for patrolling;
    
    [HideInInspector] public Transform target; //Find the Player;


    [HideInInspector] public GameObject heartLeftOne, heartLeftTwo, heartMiddle, heartRightTwo, heartRightOne; //Enemy hearts;
    public GameObject damageText; //Damage Text Popup;
    public GameObject goldPrefab; //Gold Prefab;
    public GameObject enemyHearts; //All enemy Hearts stored in a Empty gameobject for a much easy way to deactivate them;
    public GameObject hitBox; //Where we check if player entered in attackRange for enemy;
    public GameObject hotZone; //Hotzone is where enemy will follow the player;
    public GameObject triggerArea; //TriggerArea detect if player entered in Range of Enemy AI;

    public bool cooling; //Check if Enemy is cooling after attack
    public bool attackMode; //Check when enemy is in Attack mode or not;
    public bool facingRight; //If FacingRight is true or false;
    public bool playerInRange; //If Player is in Range set the bool true;
    
    [HideInInspector] public bool inRange; //Check if Player is in TriggerArea Range;

    public float attackDistance; //Minimum distance for attack
    public float moveSpeed; //Speed of enemy;
    public float timer; //Timer for cooldown between attacks
    
    #endregion

    #region Private Variables
    
    private Animator anim; //Animator;
    
    private float distance; //Store the distance b/w enemy and player
    private float intTimer; // Cooldown b/w enemy attack;
    private float textPositionX = 0.5f; //Position X for Damage Text;
    private float textPositionY = 1.5f; //Position Y for Damage Text;
    private float goldPositionY = 0.5f; //Gold Positon Y when Enemy Die;
    private float localScaleX = 0.5f; //Scale X for Gold Prefab;
    private float localScaleY = 0.5f; //Scale Y for Gold Prefab;

    private Experience_System experience; //Reference to Leveling system;
    private Player_Health player; //Reference for Player Health;
    
    #endregion

    //Function Awake;
    public void Awake()
    {
        //Check what limit is closer for patrolling;
        SelectTarget();

        intTimer = timer; //Store the inital value of timer
        anim = GetComponent<Animator>(); //Animator;
    }

    //Start Function;
    public void Start ()
    {
        //Start health for enemy;
        currentHealth = maxHealth;

        //Hearts for enemy;
        heartRightOne.SetActive(true);
        heartRightTwo.SetActive(true);
        heartMiddle.SetActive(true);
        heartRightTwo.SetActive(true);
        heartLeftOne.SetActive(true);

        //Find The GameObject and Take the Scripts Function;
        player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player_Health>();
        experience = GameObject.FindGameObjectWithTag("Experience").GetComponent<Experience_System>();
    }
    
    //Update Function;
    public void Update()
    {
        if (!attackMode)
        {
            Move();
        }

        if (!InsideOfLimits() && !inRange)
        {
            SelectTarget();
        }

        if (inRange)
        {
            EnemyLogic();
        }
    }

    public void EnemyLogic()
    {
        distance = Vector2.Distance(transform.position, target.position);

        if (distance > attackDistance)
        {
            StopAttack();
        }
       
        else if (attackDistance >= distance && cooling == false)
        {
            Attack();
        }

        if (cooling)
        {
            Cooldown();
            anim.SetBool("Attack", false);
        }
    }

    public void Move()
    {
        anim.SetBool("Walk", true);

        Vector2 targetPosition = new Vector2(target.position.x, transform.position.y);

        transform.position = Vector2.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);
    }

    public void Attack()
    {
        timer = intTimer; //Reset Timer when Player enter Attack Range
        attackMode = true; //To check if Enemy can still attack or not

        anim.SetBool("Walk", false);
        anim.SetBool("Attack", true);
    }

    public void Cooldown()
    {
        timer -= Time.deltaTime;

        if (timer <= 0 && cooling && attackMode)
        {
            cooling = false;
            timer = intTimer;
        }
    }

    public void StopAttack()
    {
        cooling = false;
        attackMode = false;
        anim.SetBool("Attack", false);
    }

    public void TriggerCooling()
    {
        cooling = true;
    }

    //Enemy Take Damage;
    public void TakeDamage(int damage)
    {
        currentHealth -= damage;
        anim.SetTrigger("TakeHit");

        if (currentHealth == 80)
        {
            heartRightOne.SetActive(false);
        }

        else if (currentHealth == 60)
        {
            heartRightOne.SetActive(false);
            heartRightTwo.SetActive(false);
        }

        else if (currentHealth == 40)
        {
            heartRightOne.SetActive(false);
            heartRightTwo.SetActive(false);
            heartMiddle.SetActive(false);
        }

        else if (currentHealth == 20)
        {
            heartRightOne.SetActive(false);
            heartRightTwo.SetActive(false);
            heartMiddle.SetActive(false);
            heartLeftTwo.SetActive(false);
        }

        else if (currentHealth <= 0)
        {
            Die();

            heartRightOne.SetActive(false);
            heartRightTwo.SetActive(false);
            heartMiddle.SetActive(false);
            heartLeftTwo.SetActive(false);
            heartLeftOne.SetActive(false);
        }

        //Check if the enemy is facingRight or not to display the damage text popup;
        if (facingRight == true)
        {
            GameObject go = Instantiate(damageText, transform.position, Quaternion.identity);
            go.GetComponent<TextMesh>().text = damage.ToString();
            go.transform.SetParent(gameObject.transform);
            go.transform.localPosition += new Vector3(Random.Range(-textPositionX, textPositionX), Random.Range(+textPositionY, 2), Random.Range(0, 0));
        }
        
        else if (!facingRight)
        {
            GameObject go = Instantiate(damageText, transform.position, Quaternion.identity);
            go.GetComponent<TextMesh>().text = damage.ToString();
            go.transform.SetParent(gameObject.transform);
            go.transform.localPosition += new Vector3(Random.Range(-textPositionX, textPositionX), Random.Range(+textPositionY, 2), Random.Range(0, 0));
        }
    }

    //Die Funtction
    public void Die()
    {
        //Set The Animation For Dead and Stop all other functions / animations of the enemy;  
        anim.SetBool("Dead", true);
        anim.SetBool("Attack", false);
        anim.SetBool("Walk", false);

        attackMode = false;
        inRange = false;
        cooling = false;

        //Instantiate a Gold Prefab;
        GameObject gold = Instantiate(goldPrefab, transform.position, Quaternion.identity);

        gold.transform.SetParent(gameObject.transform);
        gold.transform.localPosition = new Vector2(0, goldPositionY);
        gold.transform.localScale = new Vector2(localScaleX, localScaleY);

        //Set Player +25 Experience;
        experience.IncraeseExp(25);

        //Deactivate The Enemy Collider;
        GetComponent<Collider2D>().enabled = false;

        //Destroy The Enemy AI;
        Destroy(hotZone);
        Destroy(triggerArea);
        Destroy(hitBox);
        Destroy(enemyHearts);
    }

    //Check if enemy is inside of patrolling limits;
    public bool InsideOfLimits()
    {
        return transform.position.x > leftLimit.position.x && transform.position.x < rightLimit.position.x;
    }

    //Function For Patrolling state;
    public void SelectTarget()
    {
        float distanceToLeft = Vector3.Distance(transform.position, leftLimit.position);
        float distanceToRight = Vector3.Distance(transform.position, rightLimit.position);

        if (distanceToLeft > distanceToRight)
        {
            target = leftLimit;
        }
        
        else if (distanceToLeft < distanceToRight)
        {
            target = rightLimit;
        }

        Flip();
    }

    //Function for flip the enemy;
    public void Flip()
    {
        Vector3 rotation = transform.eulerAngles;
        
        if (transform.position.x < target.position.x)
        {
            rotation.y = 180;
            facingRight = true;
        }
        
        else
        {
            rotation.y = 0;
            facingRight = false;
        }

        transform.eulerAngles = rotation;
    }

    //If Player enter the trgger where enemy can attack set bool to true;
    public void OnTriggerEnter2D (Collider2D collision)
    {
        if (collision.gameObject.tag.Equals("Player"))
        {
            playerInRange = true;
        }
    }

    //If Player leave the trigger where enemy can attack set bool back to false;
    public void OnTriggerExit2D (Collider2D collision)
    {
        if (collision.gameObject.tag.Equals("Player"))
        {
            playerInRange = false;
        }
    }

    // Player will take damage from enemy if the player is in Range of enemy for attack;
    public void PlayerTakeDamage ()
    {
        if (playerInRange == true)
        {
            player.TakeDamage();
        }

        else if (playerInRange == false)
        {
            Debug.Log("Player not in Range!");
        }
    }
}

Because the script is done this way, when I go to attack the second enemy, I can't kill him anymore If help I can make a video to show the entire script bcs its a big one and to show exactly the problem

\$\endgroup\$
13
  • \$\begingroup\$ Can you include the declaration of your class-level variables (like currentHealth, heartRightOne, etc.) in your question? I suspect that your mistake might be there. \$\endgroup\$ – Philipp Jan 11 at 10:07
  • \$\begingroup\$ You are probably still trying to affect your killed enemy, you are not affecting your current enemy. Please put some logs into your code to see which enemy you are trying to hurt. \$\endgroup\$ – Çağatay IŞIK Jan 11 at 10:11
  • \$\begingroup\$ @Philipp I editet the post and put all the enemy script \$\endgroup\$ – Kymbers Jan 11 at 10:14
  • \$\begingroup\$ @ÇağatayIŞIK Yes, Now I Run another test, and Yea, after I kill first enemy and go to the second one, my character still trying to affect the first enemy which is already dead \$\endgroup\$ – Kymbers Jan 11 at 10:17
  • \$\begingroup\$ OK, you did not declare your variables as static and you don't seem to Find any sub-objects at runtime. That leaves the question how you set up things in the editor. Are all those other objects you are referencing children of your prefab? Is your enemy a prefab? \$\endgroup\$ – Philipp Jan 11 at 10:20
1
\$\begingroup\$

The problem is actually in the Player_Combat.cs script which isn't in the question and only visible shortly in the linked video at 3:20.

It has a variable private Enemy enemy which it obtains once in Start and then never changes. That makes the player unable to interact with any other enemy except the one it found at the beginning of the game.

Instead of having the concept of a "current enemy", I would recommend to rather check what's in front of the player the moment when they attack, see if any of those objects are still living enemies, and then damage them.

One method you can use to detect colliders which overlap a given area is Physics2D.OverlapAreaAll (or perhaps another of the Overlap* methods of class Physics2D). This method returns an array of all colliders which overlap a rectangular area. But keep in mind that not all of these colliders might be enemies, and not all of these enemies might still be alive.

Untested code:

Collider[] detectedColliders = Physics2D.OverlapAreaAll(/* A box in front of the current player position*/);
foreach (Collider detectedCollider in detectedColliders) {
    Enemy detectedEnemy = detectedCollider.GetComponent<Enemy>();
    if(detectedEnemy != null && detectedEnemy.currentHealth > 0) { 
          detectedEnemy.TakeDamage(damage);
    }
}

Alternatively, if you don't want the player to be able to engage more than one enemy at a time and keep the concept of a single "current enemy", then you need to implement the code for changing that variable to a different enemy when appropriate and also need to handle the case that there might be times when there isn't any enemy at all in the game.

\$\endgroup\$
2
  • \$\begingroup\$ Omg, it worked, now is working fine, everything working, thank you a lot!!! And thanks to everyone who tried to help me!!! \$\endgroup\$ – Kymbers Jan 11 at 15:55
  • \$\begingroup\$ P.S: I spent 5 days to figure out why this attack system don't work, thank you again all!!! \$\endgroup\$ – Kymbers Jan 11 at 15:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.