- Home /
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);
}
}
Answer by Bunny83 · Jan 07, 2012 at 01:37 AM
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);
}
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.