Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

OverlapSphere only works for the first 2 enemies

Discussion in 'Scripting' started by Ionselat, Mar 18, 2022.

  1. Ionselat

    Ionselat

    Joined:
    Oct 2, 2014
    Posts:
    2
    I have a spawner that create enemies with this script on them. OverlapSphere, works for only the first two spawns, and from there the future enemies don't ever engage the player when he comes in range. If I take out OverlapSphere the enemies work every time. Am I doing something wrong here? The commented out DrawSphere will draw the spheres around the enemies, so it seems like everything should work. No errors appear in the log either. Here is my script.

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.AI;
    6. public class Enemy : MonoBehaviour
    7. {
    8.     public float attackSpeed = 2f;
    9.     public float attackDamage = 2f;
    10.     public float HP = 100f;
    11.     public float aggroDistance = 5.5f;
    12.     public LayerMask layerMask;
    13.     private Player player;
    14.     private NavMeshAgent agent;
    15.     private float nextAttack;
    16.     private Collider[] colliders;
    17.     // Start is called before the first frame update
    18.     void Start()
    19.     {
    20.         agent = GetComponentInChildren<NavMeshAgent>();
    21.         agent.updateRotation = true;
    22.         agent.updatePosition = true;
    23.        
    24.         player = GameObject.Find("Player").GetComponent<Player>();
    25.         //this.gameObject.SetActive(true);
    26.     }
    27.     // Update is called once per frame
    28.     void Update()
    29.     {
    30.         //checksphere and overlapsphere works only for the first spawn
    31.         //DrawSphere shows the spheres in scene mode when enemies spawn
    32.         //isAggro = Physics.CheckSphere(this.transform.position, aggroDistance, layerMask);
    33.         colliders = Physics.OverlapSphere(this.transform.position, aggroDistance, layerMask);
    34.         if (colliders.Length > 0)
    35.         {
    36.             agent.SetDestination(player.transform.position);
    37.             if (agent.remainingDistance <= agent.stoppingDistance)
    38.             {
    39.                 nextAttack -= Time.deltaTime;
    40.                 if (nextAttack <= 0 && HP > 0)
    41.                 {
    42.                     //Does Damage before getting close
    43.                     if (player.isCloseToEnemy)
    44.                     {
    45.                         Attack();
    46.                         nextAttack = attackSpeed;
    47.                     }
    48.                 }
    49.             }
    50.         }
    51.        
    52.         if (HP <= 0)
    53.         {
    54.             Destroy(this.gameObject);
    55.             player.isFighting = false; //Should I be setting this here?
    56.             player.isCloseToEnemy = false; //Should I be setting this here?
    57.             print("Player is no longer fighting");
    58.         }
    59.     }
    60.     void Attack()
    61.     {
    62.         print(this.tag + " is attacking " + player.tag);
    63.         player.HP -= attackDamage;      
    64.     }
    65.     //private void OnDrawGizmos()
    66.     //{
    67.     //    Gizmos.color = Color.blue;
    68.     //    Gizmos.DrawSphere(this.transform.position, aggroDistance);
    69.     //}
    70. }
    71.  
     
  2. JavaBob

    JavaBob

    Joined:
    Apr 24, 2018
    Posts:
    19
    I think the problem is at line 37: if (agent.remainingDistance <= agent.stoppingDistance), because internally it is not that accurate, you should add an offset, like "if (agent.remainingDistance <= agent.stoppingDistance + 0.2f)".
     
  3. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,572
    I see a huge issue with this line:

    Code (CSharp):
    1. player.isCloseToEnemy = false;
    Look, almost all values you're dealing with in your script are instance variables of your enemy (HP, agent, nextAttack, ...). It's also perfectly fine to read a shared resource like the player position. However having enemies changing a shared state on the player calls for trouble. We don't know when you set this variable to true because that's probably in a different script (most likely in the player script itself).

    Likewise this check

    Code (CSharp):
    1. if (player.isCloseToEnemy)
    would also cause issues because again, it's a shared state on the player. So if 3 enemies are within the aggro distance and only one of them gets close enough and isCloseToEnemy gets set to true, all 3 enemies would attack the player at once. Each of your enemy should have its own closeToPlayer variable. Maybe the player needs such an information as well, however a single boolean is ambiguous because, again, there could be serveral enemies in range at the same time. On the player side you may want to hold a list of the enemies which are close so you can refer to them when needed.

    Though it's hard to give suggestions without seeing the full picture how your different classes are supposed to interact.

    I would also recommend to implement some sort of "ApplyDamage" method both on your player and your enemy. Having one class directly messing around with the variables of another class is generally a bad idea. When an enemy wants to hurt the player, it would call ApplyDamage and pass the amount of damage. Inside the method the player could do some sanity checks and for example if you plan to include an invincibility powerup, that only need to be implemented in that one method. So no matter where the damage comes from it can be intercepted inside the method. Also the Player class would be the only class which directly modifies its own variables. Of course indirectly it's still the enemy, but through a controlled logic flow in the player class.

    Something like the ApplyDamage method would also be the right place for check if the player (or enemy) is dead. If that method is the only place where the HP can be reduced, it's pretty pointless to check the HP every frame if it's below 0.
     
  4. Ionselat

    Ionselat

    Joined:
    Oct 2, 2014
    Posts:
    2
    Thanks for your help, both of you. The stopping distance is needed or it pushes my character around. I moved the onTriggerEnter portion of the player script that set the isCloseToEnemy variable into the enemy script and renamed it to isCloseToPlayer. I also managed to figure out there was an error with the way I was handling the player movement, I was using a navMeshAgent to move the character, and when I changed the movement to use a Character Controller the issue with OverlapSphere was resolved.