Search Unity

Resolved OnCollisionEnter seems to be happening twice?

Discussion in 'Scripting' started by BenevolenceGames, May 5, 2021.

  1. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    128
    I am trying to write a trial health, score, and ammo GUI, but for some reason my OnCollisionEnter events seem to be firing twice. Have a look --
    GifMaker_20210505161818723.gif

    -- As you can see from the GIF, the enemiesAlive is subtracting 2 per kill instead of once... I have a debug statement that plays twice as well....

    -- Here is the relevant code from my EnemyController Script.
    Code (CSharp):
    1.     void Update()
    2.     {
    3.         transform.LookAt(GameObject.Find("Player").transform);
    4.         Vector3 heading = GameObject.Find("Player").transform.position - transform.position;
    5.         myRB.velocity = heading.normalized * GameManager.current.enemyMoveSpeed;
    6.     }
    7.     private void OnCollisionEnter(Collision collision)
    8.     {
    9.         if (collision.gameObject.CompareTag("Bullet"))
    10.         {
    11.             Debug.Log("You killed that guy!");
    12.             gameObject.SetActive(false);
    13.             GameManager.current.enemiesAlive -= 1;
    14.         }
    15.     }
    16. }
    17.  


    -- Any help here understanding this would be greatly appreciated.
     
  2. oldhighscore

    oldhighscore

    Joined:
    Nov 28, 2020
    Posts:
    79
    How many "You killed that guy!" statements are being logged out per hit?
    What is your GameManager object being used?
    Does this issue occur when there is only one enemy or does it occur when there only multiple enemies?
     
  3. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    I had similar problem few months ago, are you sure there is only on enemy being hit and then is bullet destroyed?
    How you destroy bullet on that collision?Is it that deactivation?

    Better use Destroy(gameObject); imo
     
  4. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    128
    It is being deactivated. I am using an Object Pool because eventually these concepts will be ported into a magic based bullet hell game, so the objects will be pretty hefty with particle effects and the like, I thought better to preload and pool 15 of them than to instantiate and destroy.

    Bullet Pooling code --
    Code (CSharp):
    1. public class BulletPool : MonoBehaviour
    2. {
    3.     public static BulletPool current;
    4.     public GameObject bullet;
    5.  
    6.     public int amountToPool = 10;
    7.     public List<GameObject> pooledBullets;
    8.  
    9.     public bool poolWillGrow = true;
    10.  
    11.     private void Awake()
    12.     {
    13.         current = this;
    14.     }
    15.  
    16.     private void Start()
    17.     {
    18.         pooledBullets = new List<GameObject>();
    19.         for (int i = 0; i < amountToPool; i++)
    20.         {
    21.             GameObject obj = GameObject.Instantiate(bullet);
    22.             obj.SetActive(false);
    23.             pooledBullets.Add(obj);
    24.         }
    25.     }
    26.  
    27.     public GameObject GetPooledBullet()
    28.     {
    29.         for (int i = 0; i < pooledBullets.Count; i++)
    30.         {
    31.             if (!pooledBullets[i].activeInHierarchy)
    32.             {
    33.                 return pooledBullets[i];
    34.             }
    35.         }
    36.  
    37.         if (poolWillGrow)
    38.         {
    39.             GameObject obj = GameObject.Instantiate(bullet);
    40.             pooledBullets.Add(obj);
    41.             return obj;
    42.         }
    43.  
    44.         return null;
    45.     }
    46.  
    47.     private void FixedUpdate()
    48.     {
    49.      
    50.     }
    51. }
    52.  
    --Bullet Controller Full

    Code (CSharp):
    1. public class BulletController : MonoBehaviour
    2. {
    3.     private void OnEnable()
    4.     {
    5.         Invoke("KillBullet", 5.0f);
    6.     }
    7.  
    8.     private void KillBullet()
    9.     {
    10.         gameObject.SetActive(false);
    11.     }
    12.     private void FixedUpdate()
    13.     {
    14.         transform.localPosition = transform.localPosition + transform.forward * GameManager.current.bulletSpeed * Time.deltaTime;
    15.     }
    16.     private void OnDisable()
    17.     {
    18.         CancelInvoke();
    19.     }
    20.     public void OnCollisionEnter(Collision collision)
    21.     {
    22.         KillBullet();
    23.     }
    24.  
    25. }
     
  5. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    128
    It happens in Every case, only one enemy, multiple enemies. Each hit runs the OnCollisionEnter method in it's entirety. It prints "You killed that guy!" twice, btw, sorry I didn't include that. All evidence points towards getting a collision twice from one object... I'm not exactly sure why. The GameManager is attached to an empty GameObject positioned at the Global center btw. It act's mainly as a progress advancement script and a data house.
     
  6. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    I am using similar system with bullets

    1)Script on bullet
    Code (CSharp):
    1.  
    2.  private void OnCollisionEnter2D(Collision2D collision)
    3.         {
    4.             //Minerals collision
    5.             var mineral = collision.collider.GetComponent<MineralBehaviour>();
    6.             var player = collision.collider.GetComponent<PlayerCollisions>();
    7.             //   var loot = collision.collider.GetComponent<LootBehaviour>();
    8.             var bulletCollision = collision.gameObject.tag;
    9.  
    10.             if (mineral)
    11.             {
    12.  
    13.  
    14.                 string mineralMaterial;
    15.                 if (collision.gameObject.tag == "Soil")
    16.                 {
    17.                     mineralMaterial = "Soil";
    18.  
    19.                 }
    20.                 else if (collision.gameObject.tag == "Iron")
    21.                 {
    22.                     mineralMaterial = "Iron";
    23.                 }
    24.                 else if (collision.gameObject.tag == "Silver")
    25.                 {
    26.                     mineralMaterial = "Silver";
    27.                 }
    28.                 else if (collision.gameObject.tag == "Gold")
    29.                 {
    30.                     mineralMaterial = "Gold";
    31.                 }
    32.                 else if (collision.gameObject.tag == "Diamonds")
    33.                 {
    34.                     mineralMaterial = "Diamonds";
    35.                 }
    36.                 else return;
    37.  
    38.                 Destroy(gameObject);
    39.  
    40.                 mineral.HitMineral(mineralMaterial, 1);
    41.                 playerInput.isBulletFlying = false;
    42.                 StaticMaterials.AddFloatValue("miningExperience", 0.3f);
    43.  
    44.  
    45.  
    46.             }
    47.             else if
    48.  
    49.                   (
    50.                            bulletCollision == "Loot_Soil" ||
    51.                            bulletCollision == "Loot_Iron" ||
    52.                            bulletCollision == "Loot_Silver" ||
    53.                            bulletCollision == "Loot_Gold" ||
    54.                            bulletCollision == "Loot_Diamond"
    55.                            )
    56.  
    57.  
    58.             {
    59.                 Physics2D.IgnoreCollision(gameObject.GetComponent<Collider2D>(), collision.gameObject.GetComponent<Collider2D>());
    60.  
    61.             }
    62.         }
    63.  
    64. }
    2)Script on mineral
    Code (CSharp):
    1.  public void HitMineral(string material, int damage)
    2.         {
    3.  
    4.             int randomSprite = Random.Range(1, 4);
    5.             hitpoints -= damage;
    6.             if (hitpoints == 4)
    7.             {
    8.                 gameObject.GetComponent<SpriteRenderer>().sprite = sprites[1];
    9.             }
    10.             else if (hitpoints == 3)
    11.             {
    12.                 gameObject.GetComponent<SpriteRenderer>().sprite = sprites[2];
    13.  
    14.             }
    15.             else if (hitpoints == 2)
    16.             {
    17.                 gameObject.GetComponent<SpriteRenderer>().sprite = sprites[3];
    18.  
    19.             }
    20.             else if (hitpoints == 1)
    21.             {
    22.                 gameObject.GetComponent<SpriteRenderer>().sprite = sprites[4];
    23.  
    24.             }
    25.             else if (hitpoints == 0)
    26.             {
    27.                 Destroy(gameObject);
    28.                 int lootMaterial;
    29.                 string lootTag;
    30.                 //Vector3 lootOffset = new Vector3(0.2f, 0, 0);
    31.  
    32.                 if (material == "Soil")
    33.                 {
    34.                     lootMaterial = 0;
    35.                     lootTag = "Loot_Soil";
    36.                 }
    37.                 else if (material == "Iron")
    38.                 {
    39.                     lootMaterial = 1;
    40.                     lootTag = "Loot_Iron";
    41.                 }
    42.                 else if (material == "Silver")
    43.                 {
    44.                     lootMaterial = 2;
    45.                     lootTag = "Loot_Silver";
    46.                 }
    47.                 else if (material == "Gold")
    48.                 {
    49.                     lootMaterial = 3;
    50.                     lootTag = "Loot_Gold";
    51.                 }
    52.                 else if (material == "Diamonds")
    53.                 {
    54.                     lootMaterial = 4;
    55.                     lootTag = "Loot_Diamonds";
    56.                 }
    57.                 else return;
    58.  
    59.                 GameObject spawnedLoot = Instantiate(loot, transform.position, transform.rotation) as GameObject;
    60.                 // loot.transform.SetParent(GameObject.Find("Grid").transform, true);
    61.                 StaticMaterials.SpendValue("stamina", StaticMaterials.staminaSpendModifier);
    62.                 spawnedLoot.GetComponent<SpriteRenderer>().material = materials[lootMaterial];
    63.                 spawnedLoot.tag = lootTag;
    64.  
    65.                 ////Debug placeHolder
    66.                 //GameObject placeholder = Instantiate(debugPlaceHolder, transform.position, transform.rotation) as GameObject;
    67.                 //// loot.transform.SetParent(GameObject.Find("Grid").transform, true);
    68.                 //StaticMaterials.SpendValue("stamina", StaticMaterials.staminaSpendModifier);
    69.                 //spawnedLoot.GetComponent<SpriteRenderer>().material = materials[lootMaterial];
    70.                 //spawnedLoot.tag = lootTag;
    71.  
    72.                 hitpoints = 5;
    73.             }
    74.             else return;
    75.         }

    3)Maybe it can help you each hit mineral take damage , when 0 hp mineral is destroyed.So you can cut part you need it is working with no issue. It is not very nice script but i am currently working on some other things in my game and this is one of my first but it is working :)
    btw about that preloaded bullets, it is not really needed you no need to worry about this i am instantiating thousands of go in scene and there is no problem def not with 15 bullets :)
     
    Last edited: May 5, 2021
  7. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    How many colliders are on the enemy and how many colliders are on the player? If there are multiple colliders it can trigger once per hit.

    As a side note - GameObject.Find is very slow. Calling it once per frame is bad enough, but twice per frame per enemy is excessive. Why not just find the player once in Start and reuse the same reference?
     
  8. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    128
    The player collider shouldn't have any bearing on this particular issue. Just the collider on the bullet and the enemy GameObjects.

    Yes, searching twice is dumb. Especially for the same GameObject to get different component properties. Noted, will change. On closer inspection, though, they only have one collider each.

    -- The Enemy prefab placeholder

    Screenshot (6).png

    --The bullet prefab
    Screenshot (8).png


    -- As you can see the Enemy prefab doesn't even have a collider. I'm still at a loss with this. It's putting a giant damper on the progress at this point.
     
  9. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    128
    Oh man. I'm dumb. You were right PraetorBlue. I forgot the enemy is a compund object with 2 primitives. The head sphere, and the body capsule. Each of those were carrying a collider.
     
    PraetorBlue likes this.