x


Problem with using IEnumerator for method, c#

So in order to use 'yield return new WaitForSeconds();' I needed to return IEnumerator for my PlayerFire(); method in order to not get compiler errors. However, now that I can play the game, the method doesn't seem to do anything though it is still being called. I'm not clear on why this is and any explanation or a better way to do this without using WaitForSeconds function would be helpful.

using UnityEngine;
using System.Collections;

public class ShipControls : MonoBehaviour 
{
    public bool pausedState;
    public float speed;
    public float nextFire;
    public float fireRate;
    public float fireRate2;
    public int playerLives;
    public int powerup;
    public int shootForce;
    private Vector3 moveDirection;
    public Rigidbody shipProjectile;
    public Rigidbody shipProjectile1;
    public Rigidbody shipProjectile2;
    public Rigidbody shipProjectile3;
    public GameObject playerShip;

    void Awake()
    {
       pausedState = false;
       speed = 3.0F;
       nextFire = 0.0F;
       fireRate = 0.25F; //allows for up to 4 shots per second. Used for GetButtonDown.
       fireRate2 = 0.5F; //asllows for up to 2 shots per second. Used for GetButton.
       playerLives = 3;
       powerup = 0;
       shootForce = 10;
       moveDirection = Vector3.zero;
    }

    void Update() 
    {
       if(gameObject.tag == "Player")
       {
         if(pausedState == false)
         {
          //movement controls
          CharacterController controller = GetComponent<CharacterController>();
          moveDirection = new Vector3(0, Input.GetAxis("Vertical"), 0);
          moveDirection = transform.TransformDirection(moveDirection);
          moveDirection *= speed;
          controller.Move(moveDirection * Time.deltaTime);
          //weapon controls
          if(Input.GetButtonDown("Fire1") && Time.time > nextFire)
          {
              nextFire = Time.time + fireRate;
              PlayerFire();  
          }
          else if(Input.GetButton("Fire1") && Time.time > nextFire)
          {
              nextFire = Time.time + fireRate2;
              PlayerFire();
          }
          if(Input.GetKeyDown(KeyCode.F)) powerup = 1;
          if(Input.GetKeyDown(KeyCode.G)) powerup = 2;
         }
       }
    }

    void OnControllerColliderHit(ControllerColliderHit hit)
    {
       if(hit.collider.gameObject.CompareTag("Enemy"))
       {
         Destroy(hit.collider.gameObject);
         Destroy(gameObject);
         playerLives --;

         Instantiate(playerShip);
       }


    }

    IEnumerator PlayerFire()
    {
       if(powerup == 0)
       {
         shipProjectile = shipProjectile1;
       }
       else if(powerup == 1)
       {
         shipProjectile = shipProjectile2;
         yield return new WaitForSeconds(5);
         powerup = 0;
       }
       else if(powerup == 2)
       {
         shipProjectile = shipProjectile3;
         yield return new WaitForSeconds(5);
         powerup = 0;
       }
       Rigidbody clone;
       clone = Instantiate(shipProjectile, transform.position + new Vector3(1, 0, 0), transform.rotation) as Rigidbody;
       clone.rigidbody.AddForce(transform.forward * shootForce);
    }
}
more ▼

asked Jan 07 '12 at 12:41 AM

Apples_mmmmmmmm gravatar image

Apples_mmmmmmmm
188 5 9 17

(comments are locked)
10|3000 characters needed characters left

1 answer: sort voted first

Coroutines are not normal functions. They are so called generator--functions which "generates" or create a special object which is returned by the function. This object can be used to iterate through your coroutine, in other words: to execute it. This iteration is done by Unity's coroutine scheduler, but you have to "hand over" the generated object to Unity. This is done by calling StartCoroutine() with the generated object as parameter.

So far, that's how coroutines work. In your case however it makes no sense. You have a firerate of 4 shots / sec. The coroutine would wait 5 seconds (if powerup == 1 or 2) before even shoot. You will have 5*4 == 20 coroutines running at the same time. Your powerup timeout check should be done elsewhere.

Those two lines looks like debugging code:

if(Input.GetKeyDown(KeyCode.F)) powerup = 1;
if(Input.GetKeyDown(KeyCode.G)) powerup = 2;

It's better to use a function or property to set / activate a powerup.

void SetPowerUp(int aPowerUpType)
{
    powerup = aPowerUpType;
    switch (aPowerUpType)
    {
        case 1:
            StartCoroutine(ResetPowerUp(5.0f));
        break;
        case 2:
            StartCoroutine(ResetPowerUp(5.0f));
        break;
        default:
        break;
    }
}
IEnumerator ResetPowerUp(float aWaitTime)
{
    yield return new WaitForSeconds(aWaitTime);
    powerup = 0;
}

and then use it like this

if(Input.GetKeyDown(KeyCode.F)) SetPowerUp(1);
if(Input.GetKeyDown(KeyCode.G)) SetPowerUp(2);

edit ps, you might want check if you already use a powerup atm. If you start two coroutines both would set powerup to 0 after the waittime. This will propably overlap. To be able to "retrigger" the timeout you better work with timeout values which can be changed at any time.

private float m_PowerUpTimeOut = 0.0f;

void SetPowerUp(int aPowerUpType)
{
    powerup = aPowerUpType;
    switch (aPowerUpType)
    {
        case 1:
            m_PowerUpTimeOut = Time.time + 5.0f;
        break;
        case 2:
            m_PowerUpTimeOut = Time.time + 5.0f;
        break;
        default:
            m_PowerUpTimeOut = 0.0f;
        break;
    }
}

and in Update do your timeout check:

if (powerup != 0 && Time.time >= m_PowerUpTimeOut)
{
    SetPowerUp(0);
}
more ▼

answered Jan 07 '12 at 01:37 AM

Bunny83 gravatar image

Bunny83
45.4k 11 49 207

Thanks, that cleared up my confusion. All the examples I had seen of how to use IEnumerator were pretty unclear to me, and it was very helpful to have everything explained properly. Also, thank you for suggesting a much more elegant way of doing things.

Jan 07 '12 at 05:18 AM Apples_mmmmmmmm
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x4157
x170
x66
x55

asked: Jan 07 '12 at 12:41 AM

Seen: 2328 times

Last Updated: Jan 07 '12 at 05:18 AM