How to make OnTriggerEnter2D work again upon the GameObject being disabled and then re-enabled?

In the game I’m working on, I am noticing something strange that I do not want to happen. Whenever I have the player attack what is infront of them, it only works once. If the enemy stands still, and the player stands still while attacking, the player can not harm the enemy after the first swing. Either the enemy or the player has to move again in order for the attack to actually inflict damage upon said enemy. What I want to know is how to fix this, so I can have both the player and enemy stand still and still be able to inflict damage upon one-another.

What I have for an “attack” is simple. The player approaches the enemy, clicks, and that stops the player’s movement while the attack animation plays, which enables an object that is a child object of the Player, with this object specifically being a slash effect. This object has a trigger attached to it, which activates on any enemy it touches, supposedly. The slash also has a script attached to it which does the actual harming, said script being called “HurtEnemy”, which can be seen here:
HurtEnemy Script

using UnityEngine;
using System.Collections;

public class HurtEnemy : MonoBehaviour {

    private int damageToDeal;
    public Transform hitPoint;
    public GameObject damageNumber;

    private bool justCrit;
    private int baseDamage;
    public int critChance;

    private PlayerStats pATK;

    void Start () {
        pATK = FindObjectOfType<PlayerStats> ();
        damageToDeal = 0;
        baseDamage = 0;
    }

    void Update () {
      
    }

    void OnTriggerEnter2D(Collider2D other) {
        if (other.gameObject.tag == "Enemy") {
            baseDamage = pATK.currentATK;
            damageToDeal = baseDamage;

            damageToDeal += Random.Range (-1, 2); //Adds a bit of randomness to damage dealt.

            if (Random.Range (1, 100) <= critChance) { //Possibility of crit, exact chance able to be changed via Unity interface.
                damageToDeal *= 2;
                justCrit = true;
            }

            other.gameObject.GetComponent<EnemyHealthManager> ().HurtEnemy (damageToDeal);

            var clone = (GameObject)Instantiate (damageNumber, hitPoint.position, Quaternion.Euler (Vector3.zero));

            if (justCrit) {
                clone.GetComponent<FloatingNumbers> ().damageNumber = damageToDeal;
                clone.GetComponent<FloatingNumbers> ().displayNumber.color = Color.yellow;
                justCrit = false;
            } else {
                clone.GetComponent<FloatingNumbers> ().damageNumber = damageToDeal;
                clone.GetComponent<FloatingNumbers> ().displayNumber.color = Color.white;
            }

            damageToDeal = baseDamage;
        } else if (other.gameObject.tag == "Destructable") {
            other.gameObject.GetComponent<DestructableObjectManager> ().KillObject ();
        }
    }
}

The actual damage is dealt when this script calls another script on the object it touches, called “EnemyHealthManager”, specifically a “HurtEnemy” function. The script in question can be seen here:
EnemyHealthManager Script

using UnityEngine;
using System.Collections;

public class EnemyHealthManager : MonoBehaviour {

    public int enemyMaxHealth;
    public int enemyCurrentHealth;

    private PlayerStats thePlayer;

    public int expUponDeath;

    void Start () {
        enemyCurrentHealth = enemyMaxHealth;

        thePlayer = FindObjectOfType<PlayerStats> ();
    }

    void Update () {
        if (enemyCurrentHealth <= 0) {
            thePlayer.AddExperience (expUponDeath);
            Destroy (gameObject);
        }
    }

    public void HurtEnemy(int damage) {
        enemyCurrentHealth -= damage;
        StartCoroutine ("HurtColor");
    }

    public void MaxHealth() {
        enemyCurrentHealth = enemyMaxHealth;
    }

    IEnumerator HurtColor() {
        for (int i = 0; i < 3; i++) {
            GetComponent<SpriteRenderer>().color = new Color (0.8f, 0.6f, 0.8f);
            yield return new WaitForSeconds (.1f);
            GetComponent<SpriteRenderer>().color = Color.white;
            yield return new WaitForSeconds (.1f);
        }
    }
}

This health manager deals with the enemy’s, well, health. Once the enemy’s HP reaches zero, the gameobject of said enemy is completely destroyed, and the player given a set amount of experience.

With all that out of the way, it sounds like everything should be perfectly fine, right? I’d think so. Except for the one issue that is still bugging me non-stop, the OnTriggerEnter2D on the slash object (the one with the “HurtEnemy” script) does not activate a second time, even once the slash object’s active variable is turned on and then off. How can I make it so everytime the player attacks, the OnTriggerEnter2D works again upon the slash being re-enabled?

Put a debug line in OnTriggerExit2D to check it is actually firing. If the two colliders are close enough without movement you may find Exit isn’t being called which then prevents Enter firing again.

Alright, I’ll try that when I can, but what if that’s why Enter is being prevented from firing? What can I do then to fix it? Any suggestions/ideas?

Depends on how your characters are set up. You need to guarantee the collider is leaving the enemy collider after each attack. Try pausing once you’ve hit attack and stepping through each frame in scene view to check.

Alternatively you could disable the collider after each hit and only enable when the attack button is pressed.

In the HurtEnemy script put this as the last line of code within OnTriggerEnter2D. Adapt it to however your objects are set up. It will disable the collider after your hit code has run.

            gameObject.GetComponent<BoxCollider2D>().enabled = false;

You’ll also need to enable it immediately after the attack button is pressed. So use the same line of code and chance false to true. Probably easier to set a public reference to the collider and use that in the scripts.

The issue is that in order for the attack collider to exit the enemy collider, either the enemy or the player have to move. So your first suggestion is practically impossible with how I have it set up. With your secondary suggestion, I just tried that, but that does not work either. Simply turning the collider off and then back on does not re-run the OnTriggerEnter2D section of the code, which is what I need to happen whenever the player attacks again.

Also, I did that debug idea from your very first suggestion, and OnTriggerExit2D is not firing, again, unless the player moves away. I’m beginning to think that the issue here is that I need to find a replacement for the OnTriggerEnter idea.

I don’t know how the attack works but if it’s a slash effect is the collider in any kind of motion? If it is could you alter it in some way so that it will always clear the enemy by retracting back or overshooting?

Disabling and enabling a collider that’s overlapping a trigger should run OnTriggerEnter2D each time it’s enabled. Or at least it does for me. Do you see the collider become unticked in the inspector after a hit and then ticked again on the next hit command?

You’ve got OnTriggerStay2D that you could use to manage a hit counter.

Let me tell you how the attack works exactly then. In my PlayerController script, I have the attack set up there. Upon clicking, it changes a variable in the animator as well as some other magic, which then causes the player to stop all motion and do the attack animation, which enables the slash game object, which has the HurtEnemy script attached. At the end of the animation, the game object of said slash is turned back off, and it only is enabled again upon the next attack.

You meant disabling the enemy collider? I thought you meant disabling the trigger. My bad. That might work, I’ll have to simply add some code and a timer to have the enemy collider do that though. However, I do see one downside to that. What if another enemy comes up to the player while the enemy collider is disabled? Will the enemy just phase through the enemy being attacked? How would I be able to stop that if the 1st enemy’s collider is disabled? Regardless, I’ll try that when I can.

OnTriggerStay2D could also work, I’d just have to set up an invincibility frames system. I haven’t had the best of luck with that in the past, but it’s not a bad idea. Again, I could try that though. I’ll give you the results when I can try it, which will probably be sometime the day after this reply was sent.

Sadly, I’ve tried both things now, neither still work for me. I guess I’ll just continue on with my game and completely revamp the player attack system later or something. :confused:

I did mean disable your slash trigger collider. A game object with a trigger collider and script attached listening for triggers should run OnTriggerEnter each time that objects collider is enabled even if already overlapping. So the script attached to your slash object could disable the slash collider after the hit code runs and enable the collider when the player clicks attack.

There’s always a variety of ways to achieve the same result. You might find it more reliable to use Ray2D for checking an enemy is within range instead of a trigger. Something like

  • player hits attack
  • run animation
  • ray cast to check enemy within range
  • if in range deal damage