byIcee byIcee - 1 month ago 11
C# Question

Instantiated Enemy outputs double damage for every enemy spawning after him

So I'm making a FPS Survival game. I made a GameManager script which handles the Enemy Spawning part of the game. My EnemyAttack script handles the enemies attacking. Problem is, when the first enemy spawns, I only take 1 damage (as set in AttackDamage), but when the second spawns, I take 2 damage (AttackDamage * 2) even if I only get into attack range of 1 enemy.
Here are all my enemy scripts:

EnemyAttack.cs:

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

public class EnemyAttack : MonoBehaviour
{
public float AttackDamage = 1.0f;
public float AttackSpeed = 1.0f;

void Start() {
StartCoroutine(Attack());
}

IEnumerator Attack() {

while (true)
{

while (PlayerMovement.isEnemyAttacking == true) {

EnemyAttacking();
yield return new WaitForSeconds(AttackSpeed);
}
yield return 0;
}

}

public void EnemyAttacking() {

Debug.Log("Enemy Attacking!");
PlayerHealth.Health -= AttackDamage;
GameObject.FindGameObjectWithTag("HealthPoints").GetComponent<Text>().text = "HEALTH: " + PlayerHealth.Health;

}

}


EnemyAI.cs:

using UnityEngine;
using System.Collections;

public class EnemyAI : MonoBehaviour {

public Transform TriggerBox;

// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update()
{

if (EnemySight.inSight == true)
{

NavMeshAgent agent = GetComponent<NavMeshAgent>();
agent.destination = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>().position;

}

}
}


EnemySight.cs:

using UnityEngine;
using System.Collections;

public class EnemySight : MonoBehaviour {

public static bool inSight = false;

// Use this for initialization
void Start()
{

}

// Update is called once per frame
void Update()
{

}

void OnTriggerEnter(Collider other)
{

if (other.gameObject.tag == "Player")
{
inSight = true;
}

}

void OnTriggerExit(Collider other) {

if (other.gameObject.tag == "Player")
{
inSight = false;
}

}

}


and PlayerHealth.cs:

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

public class PlayerHealth : MonoBehaviour {

public Transform Player;

public static float Health = 100;
public static float maxHealth = 100;

// Use this for initialization
void Start () {

GameObject.FindGameObjectWithTag("HealthPoints").GetComponent<Text>().text = "HEALTH: " + Health;

}

// Update is called once per frame
void Update () {

if (Health <= 0) {

Dead();

}
}

public static void Dead() {



}
}

Answer

There are a few problems with your scripts...

I don't think you understand what a static variable is.

A static variable shares the value of it among all instances of the class

Source

public static bool inSight;

Should not be static.

PlayerMovement.isEnemyAttacking

This should not be static as well.

Right now, as soon as you have 1 ennemy meeting the "Attack" condition, you are setting and reading a single static variable, causing all ennemies to attack. You should instead have your variable belong to every ennemy instance, by not making your variables static.

Basically, you just need to understand what a static variable is. Once you know, you will understand why your logic is not working.

EDIT: Solution

PlayerHealth script. Attach this script to your player GameObject in the scene.

using UnityEngine;
using UnityEngine.UI;

public class PlayerHealth : MonoBehaviour
{
    public static float Health;
    public static float maxHealth = 100;

    private Text healthText;


    void Start()
    {
        healthText = GameObject.FindGameObjectWithTag("HealthPoints").GetComponent<Text>();

        //Make it full 100% health on start
        Health = maxHealth;
        RefreshHealthBar();
    }



    public void TakeDamage(float damage)
    {
        Health -= damage;
        RefreshHealthBar();

        if (Health <= 0)
            Die();
    }

    public void Die()
    {
        Health = 0;
        RefreshHealthBar();

        //TODO: Your code
    }

    void RefreshHealthBar()
    {
        healthText.text = "HEALTH: " + Health;
    }
}

EnnemyAI script. Attach this to your Ennemy prefab.

 using System.Collections;
using UnityEngine;

public class EnnemyAI : MonoBehaviour
{
    public float AttackDamage = 1.0f;
    public float AttackSpeed = 1.0f;
    public float AttackRange = 1.0f;

    private bool isPlayerInSight;
    private GameObject target;
    private NavMeshAgent agent;


    // Use this for initialization
    void Start ()
    {
        target = GameObject.FindGameObjectWithTag("Player");
        agent = GetComponent<NavMeshAgent>();

        StartCoroutine(AttackLoop());
    }

    // Update is called once per frame
    void Update ()
    {
        if (isPlayerInSight)
        {
            agent.destination = target.transform.position;
        }
    }

    IEnumerator AttackLoop()
    {
        while (true)
        {
            //I don't know your attacking logic, so lets say they can attack in a 1 unit range
            while (Vector3.Distance(target.transform.position, this.transform.position) <= AttackRange)
            {
                Attack();
                yield return new WaitForSeconds(AttackSpeed);
            }

            yield return 0;
        }
    }

    void Attack()
    {
        target.GetComponent<PlayerHealth>().TakeDamage(AttackDamage);
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Player")
        {
            isPlayerInSight = true;
        }
    }

    void OnTriggerExit(Collider other)
    {
        if (other.gameObject.tag == "Player")
        {
            isPlayerInSight = false;
        }
    }
}
  1. I took the FindGameObjectWithTag out of the Update() loops for performance reasons.
  2. The player is no longer listening for attacks. Instead, the ennemy are sending damages to the player.

this way we got rid of the static variables.