Eduardo Marinho
Eduardo Marinho

Reputation: 105

How to do collision in only one target per call of the function OnTriggerToStay2D(Collider other)? (One collision per time only, instead of multiple)

I am trying to make the player hit the knife the enemy in front of him, but i only want to knife one enemy at once. The problem is, when the OnTriggerStay2D is called, every enemy that is in range of the box collider of the knife receives damage, when I want only one to receive damage. This is my script:


    public float timeBetweenKnifes = 2;
    public float timeSinceLastKnife = 0;
    public int knifeDamage = 1;
    [HideInInspector] public GameObject player;

    void OnTriggerStay2D(Collider2D collision)
    {
        timeSinceLastKnife = timeSinceLastKnife + Time.deltaTime;
        if(collision.gameObject.TryGetComponent<ZombieScript>(out ZombieScript enemyComponent) && Input.GetKey(KeyCode.E) && timeSinceLastKnife >= timeBetweenKnifes)
        {
            enemyComponent.TakeDamage(knifeDamage);
            player = GameObject.FindGameObjectWithTag("Player");
            player.GetComponent<PlayerScript>().money = player.GetComponent<PlayerScript>().money + 1;
            timeSinceLastKnife = 0;
        }
    }

Upvotes: 0

Views: 55

Answers (1)

derHugo
derHugo

Reputation: 90813

You do only attack one target call of the function OnTriggerToStay2D!

What rather happens is that OnTriggerToStay2D is called once for each overlapping object in the same frame!

You could simply have a flag

private bool alreadyCausedDamageThisFrame;
// to also prevent you timer to run faster for multiple collisions
// instead rather simply compare to a previous time rather than using a timer
private float lastKnifeTime;

void Start()
{
    // to allow immediate attack the first time
    lastKnifeTime = -timeBetweenKnifes;
}

private void FixedUpdate()
{
    // at the beginning of each physics frame reset the flag
    alreadyCausedDamageThisFrame = false;
}

void OnTriggerStay2D(Collider2D collision)
{
    // if already caused damage this frame -> ignore
    if(alreadyCausedDamageThisFrame) return;

    // without timer directly compare to previous damage time
    // if not enough time has passed -> ignore
    if(Time.time - lastKnifeTime < timeBetweenKnifes) return;

    // check input
    // if key not pressed -> ignore
    if(!Input.GetKey(KeyCode.E)) return;


    // update time
    // where exactly you do this depends a bit on your desired behavior 
    // keep it here if you want to allow to hit nothing and then have to wait for the cooldown
    lastKnifeTime = Time.time;

    // check/get component
    // if not colliding with ZombieScript -> ignore
    if(!collision.TryGetComponent<ZombieScript>(out var enemyComponent)) return;
    
    enemyComponent.TakeDamage(knifeDamage);
    var player = FindAnyObjectByType<PlayerScript>();
    player.money += 1;

    // set the flag to block any other OnTriggerStay2D call for this frame
    // ensures you can ever only hit one single enemy in this frame
    alreadyCausedDamageThisFrame = true;
    
    // if you rather only want a cooldown after you actually hit an enemy rather move the 
    //lastKnifeTime = Time.time;
    // down here
}

Upvotes: 0

Related Questions