0
\$\begingroup\$

I wanna trigger just one time and not multiple times. I have a Health System and I want that the Player takes damage from an GameObject if he enters the Trigger but it directly triggers like 65 times in a sec. how can I trigger just once ? I dont have a Script attached to the GameObject what should give the Player Damage, it just have a Box Collider 2D. The Code for the Trigger enter has the HealtScript that is attached to the Player.

Here is my HealthScript

public Image Bar;
public float maxHealth = 100f;
public float currentHealth = 0f;


// Use this for initialization
void Start () 
{
    currentHealth = maxHealth;
    InvokeRepeating ("decreaseHealth", 0f, 2f);
}


void decreaseHealth()
{
//  currentHealth -= 5f;
    float calchealth = currentHealth / maxHealth;
    SetHealth (calchealth);
}

void SetHealth (float myhealth)
{
    Bar.fillAmount = myhealth;
}

void OnTriggerEnter2D (Collider2D other)
{
    if (other.gameObject.tag == ("Damage"))
    {
        currentHealth = -5f;
    }
}

}

\$\endgroup\$
  • \$\begingroup\$ This might seem like a dumb question, but do you have exactly one collider on your Player and one collider (set with Is Trigger) on the "enemy"? Also, are you using Rigidbody colliders for both objects? \$\endgroup\$ – GorkTi200 Apr 27 '16 at 19:40
3
\$\begingroup\$

According to the Unity documentation for OnTriggerEnter2D the trigger is "[s]ent when another object enters a trigger collider attached to this object (2D physics only)". The function should only be called once per entrance.

You could gather further data by logging the number of calls to OnTriggerEnter2D and OnTriggerExit2D. Perhaps some funny business is occurring causing one object to enter then exit, then re-enter.

\$\endgroup\$
-2
\$\begingroup\$

Create a collection and store a reference of the GameObject once it enters and remove the reference once it exits.

For example, here I am utilizing HashSet.

using System.Collections.Generic;

private HashSet<GameObject> takenDamageFrom = new HashSet<GameObject>();

void OnTriggerEnter2D(Collider2D other)
{
    // If damager and not yet damaged (not contained in our references).
    if (other.gameObject.tag == "Damage" && !takenDamageFrom.Contains(other.gameObject))
    {
        currentHealth -= 5f;

        // Mark as damaged.
        takenDamageFrom.Add(other.gameObject);
    }
}

void OnTriggerExit2D(Collider2D other)
{
    // If contained in our references, remove.
    if (takenDamageFrom.Contains(other.gameObject))
    {
        takenDamageFrom.Remove(other.gameObject);
    }
}

But the idea is simple, store a reference.

If, for instance, you don't need it to apply damage on re-entry, simply do not remove the reference once the collider exits.

Keep in mind, this idea assumes that there might be multiple elements that can cause/take damage, hence the collection.
If you need it for one object only - take Uri Popov's answer, it's simpler.

\$\endgroup\$
  • \$\begingroup\$ Umm, why the downvote? \$\endgroup\$ – joltmode Apr 27 '16 at 15:15
-2
\$\begingroup\$

Use a bool variable to make sure you only enter the if once, then reset it after you have left the trigger. Or dont reset it if it has to happen exactly once.

 void OnTriggerEnter2D (Collider2D other)
        {
            if (other.gameObject.tag == ("Damage")&&!isDamaged)
            {
                currentHealth = -5f;
                isDamaged = true;
            }
        }
    void OnTriggerExit2D (Collider2D other)
    {
      isDamaged = false;
    }
\$\endgroup\$
  • 1
    \$\begingroup\$ It is likely this will not fix the problem nor change the behavior. Please revisit the Unity documentation for OnTriggerEnter and OnTriggerEnter2D. \$\endgroup\$ – user3730788 Apr 27 '16 at 15:13
  • \$\begingroup\$ Didnt helped either but the idea was not bad.. \$\endgroup\$ – NewmakerCompany Apr 27 '16 at 16:06
-2
\$\begingroup\$

You can use a coroutines for this task, you should call this coroutine in order to damage you player, so you can set a time of invencibility with the

waitForSeconds(3f);

function

bool finishedCoroutine = true;

void OnTriggerEnter2D (Collider2D other)
{
if (other.gameObject.tag == ("Damage"))
{
   {
      if(finishedCoroutine){
          finishedCoroutine = false;
          StartCoroutine("damageMyPlayer");
        }
}
}

IEnumerator damageMyPlayer ()
{

    this.currentHealth -=5f;
    yield return new WaitForSeconds(3f);
    finishedCoroutine = true; 
    print("MyCoroutine is now finished.");

}
\$\endgroup\$
  • 2
    \$\begingroup\$ The asker's problem is that OnTriggerEnter2D is being called very frequently. Starting a couroutine on every call doesn't fix that, since the fact that one coroutine is still waiting doesn't stop a new one from starting. \$\endgroup\$ – DMGregory Apr 28 '16 at 1:40
  • \$\begingroup\$ thats true, it can be fixed is you set a bool variable, i'm going to edit the code \$\endgroup\$ – Santiago murillo Apr 28 '16 at 8:30
  • \$\begingroup\$ Still wrong in two ways: 1) finishedCoroutine defaults to false so this will never damage the player. 2) Even after fixing the default, this will still damage the player every 3 seconds that the trigger continues to fire, instead of only once as OP requests. \$\endgroup\$ – DMGregory Apr 28 '16 at 16:08
  • \$\begingroup\$ you are correct at the first part, but the question asks for that the enemy damages only one time for second, so if you set the delay to 1 sec, you obtain what it is needed. \$\endgroup\$ – Santiago murillo Apr 28 '16 at 16:14
  • \$\begingroup\$ It says "how can I trigger just once" not "once per second." I can see where the ambiguity could arise where they use a rate per second when describing the symptom, but the title clarifies this by making it explicit: "just once and not multiple times" \$\endgroup\$ – DMGregory Apr 28 '16 at 16:17

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