0
\$\begingroup\$

I have a simple game in which the player can collect power-ups. Each power-up gives the player a temporary advantage for several seconds. The power-up should disappear immediately after the player collects it.

My current implementation of the power-up logic is:

[SerializeField] float duration;  

private void OnTriggerEnter2D(Collider2D other) {
    if (other.tag == "Player") {
            StartCoroutine(GiveTemporaryPowerup(other.gameObject));
            Destroy(gameObject);
    }
}

private IEnumerator GiveTemporaryPowerup(GameObject gameObject) {
    gameObject.ActivatePowerupFunction();  
    yield return new WaitForSeconds(duration);
    gameObject.DeactivatePowerupFunction();
}

This does not work: the powerup is activated but not deactivated. Deactivation does work when I remove the line "Destroy(gameObject)". However, without this line the player can collect the same powerup several times, which I want to avoid.

I tried to put the coroutine on a component attached to the player object (rather than the power-up object), but this did not solve the problem - deactivate was not called. Here is the updated code on the power-up:

private void OnTriggerEnter2D(Collider2D other) {
    if (other.tag == "Player") {
            var playerComponent = other.gameObject.GetComponent<Player>(); 
            StartCoroutine(playerComponent.GiveTemporaryPowerup(duration));
            Destroy(gameObject);
    }
}

And the code on the Player component:

public IEnumerator GiveTemporaryPowerup(float duration) {
    ActivatePowerupFunction();
    yield return new WaitForSeconds(duration);
    DeactivatePowerupFunction();
}

What is the correct way to implement this?

\$\endgroup\$
6
  • \$\begingroup\$ Putting the coroutine on an object that lives for the entire duration of the coroutine (like the player) is the correct solution to this problem. If your attempt to use this didn't work, then there's likely a mistake that we can fix. Show us how you tried running this coroutine on the player, and we can advise on what to change. \$\endgroup\$
    – DMGregory
    Commented Apr 26, 2020 at 13:29
  • \$\begingroup\$ @DMGregory I added the updated code to the question \$\endgroup\$ Commented Apr 27, 2020 at 12:10
  • \$\begingroup\$ It looks like you're still starting the coroutine on the powerup script (see how there's nothing in front of StartCoroutine? That means it's "this.StartCoroutine" instead of "playerComponent.StartCoroutine") — Am I missing something here? \$\endgroup\$
    – DMGregory
    Commented Apr 27, 2020 at 12:12
  • \$\begingroup\$ @DMGregory You are right, this was my mistake! I mistakenly thought that StartCoroutine was a global function. Changing this to "playerComponent.StartCoroutine" solved the problem. \$\endgroup\$ Commented Apr 27, 2020 at 12:16
  • \$\begingroup\$ Ah, if that solved it, want to write up your solution as an Answer below? \$\endgroup\$
    – DMGregory
    Commented Apr 27, 2020 at 12:16

1 Answer 1

0
\$\begingroup\$

My mistake was that I thought that StartCoroutine is a global function, and only the coroutine itself should belong to a living object. In fact, it is the other way around. The following code solves the problem:

[SerializeField] float duration;  

private void OnTriggerEnter2D(Collider2D other) {
    if (other.tag == "Player") {
          other.GetComponent<Player>().StartCoroutine(GiveTemporaryPowerup(other.gameObject));
          Destroy(gameObject);
    }
}

private IEnumerator GiveTemporaryPowerup(GameObject gameObject) {
    gameObject.ActivatePowerupFunction();  
    yield return new WaitForSeconds(duration);
    gameObject.DeactivatePowerupFunction();
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .