How does Unity Engine WaitForSeconds work? - c#

I'm very new to unity engine, I am trying to use waitforseconds function and it doesn't seem to be working. Any help is much appreciated.
Code:
IEnumerator SetCountText(){
countText.text = "Count: " + count.ToString();
if (count >= 12) {
winText.text = "You win!";
yield return new WaitForSeconds (4);
NextLevel ();
}
}
I call the function in start() function by using startcoroutine( setcounttext());
Thanks in advance!

You mentioned you start your couroutine in the Start() function of the unity script. Start() is only called once, when the script has first initialized.
Considering your coroutine's logic, it only starts once and ends immediately.
If constant checking is desired, what you need is enclosing everything in a while loop:
IEnumerator SetCountText(){
while (count < 12) {
countText.text = "Count: " + count.ToString();
yield return new WaitForSeconds(1);
}
winText.text = "You win!";
yield return new WaitForSeconds (4);
NextLevel ();
}
What happens now is if the count variable is >= 12 then after 4 seconds, the level changes. Not sure if that is the effect you are trying to achieve.

I have discovered that this function does not work great all of the time , so I wrote my own simple class t o sort the problem. I have never had any problems counting seconds with this one.
public class MyCouroutine
{
public static IEnumerator WaitForRealSeconds(float duration)
{
float start = Time.realtimeSinceStartup;
while (Time.realtimeSinceStartup < start + duration)
{
yield return null;
}
}
}

Related

Unity WaitForSeconds Delay not Functioning

I'm making a rhythm game and and working on a script that spawns an object on every beat of a song. Thus, it spawns a single object, waits for the length of the beat, and then spawns the next one. It's supposed to do this until the song finishes. The issue is that the script seems to be spawning everyone of the objects at once, rather than waiting for the specified amount of time. The script compiles fine and I can't figure out what the issue is. Here's my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class spawnState : MonoBehaviour
{
public float waitTime = 0.483870968f;
public bool running = true;
public int beats = 504;
private int count = 0;
public GameObject spawn;
// Update is called once per frame
void Update()
{
//Generate object
while (running == true)
{
//Wait
StartCoroutine(Wait());
//Count & Stop
count += 1;
if (count >= beats)
{
running = false;
}
}
}
//Wait function
IEnumerator Wait()
{
//Wait
yield return new WaitForSeconds(waitTime);
//Spawn
Instantiate(spawn, transform.position, spawn.transform.rotation);
}
}
Thank you!
For starters you don't want any possibly blocking while loop within Update!
Update is called once every frame.
Do you really want to instantiate 504 objects within one frame?
And then StartCoroutine does not delay the method which calls it. It rather registers a Coroutine to be running and continues immediately with the rest of the code. So you are starting 504 "parallel" running Coroutines and after 0.48.. seconds you suddenly get all 504 objects instantiated at the same time.
What you rather want to do would e.g. be either directly using a timer field without a Coroutine
private float timer;
void Update()
{
if(!running) return;
timer += Time.deltaTime;
if(timer >= waitTime)
{
timer = 0;
Instantiate(spawn, transform.position, spawn.transform.rotation);
count += 1;
if (count >= beats)
{
running = false;
}
}
}
Or if you want to use a Coroutine you could e.g. simply do
private void Start ()
{
StartCorouine (SpawnRoutine());
}
private IEnumerator SpawnRoutine ()
{
for(var count = 0; count < beats; count++)
{
yield return new WaitForSeconds (waitTime);
Instantiate(spawn, transform.position, spawn.transform.rotation);
}
}
or if Start is anyway the only place where you trigger this it can be that routine itself
private IEnumerator Start ()
{
for(var count = 0; count < beats; count++)
{
yield return new WaitForSeconds (waitTime);
Instantiate(spawn, transform.position, spawn.transform.rotation);
}
}

Counting objects with Coroutine

in my project im trying to count the diferent objects and simulate a little animation, for example i have stars in my game, and i want to count the number of stars in the final of the game from 0 trough the number of stars the user got, so i did this:
public void youWin()
{
audio.Stop ();
StartCoroutine (activatePanel ());
}
IEnumerator activatePanel()
{
yield return new WaitForSeconds (3f);
pausePanel2.SetActive (true);
for (int i = 0; i <= stars; i++) {
yield return new WaitForSeconds (0.2f);
starText2.text = i + "";
}
}
my code worked well for 0.3f on the for loop wait for seconds, but it is too slow, i want it for 0.2f, but something strange happen sometimes it get like a bug and the first number seems to go back, it doesn't count right, someone know what is happening?
It very likely that the activatePanel function is being called from another place while it is already running or the script that contains this code is attached to multiple GameObjects and the activatePanel is again, being called by another function. You can use flag to stop this from happening.
If the coroutine function is already running, use yield break; to break out of it.
bool isRunning = false;
IEnumerator activatePanel()
{
//Exit if already running
if (isRunning)
{
yield break;
}
//Not running, now set isRunning to true then run
isRunning = true;
yield return new WaitForSeconds(3f);
pausePanel2.SetActive(true);
WaitForSeconds waitTime = new WaitForSeconds(0.2f);
for (int i = 0; i <= stars; i++)
{
yield return waitTime;
starText2.text = i.ToString();
}
//Done running, set isRunning to false
isRunning = false;
}
Well i solved it with all of you guys help, actually you all where right, i thaught i was calling the youWin function just 1 time, but i forgot this is unity and i called the youWin inside a trigerEnter function, that means that the object keep enter the triger function and called the youWin function, thank you all here is what i mean with that
Solved it with the bool entered
public class Victory : MonoBehaviour {
Manager gameManager;
// Use this for initialization
public AudioClip clip;
private AudioSource audio;
public Animator girl;
private bool entered;
void OnTriggerEnter(Collider c)
{
if (c.gameObject.tag == "Player" && !entered) {
gameManager.win = true;
audio.clip = clip;
audio.Play ();
gameManager.Ball.GetComponent<MoveBall> ().enabled = true;
girl.SetBool ("win",true);
entered = true;
gameManager.youWin ();
}
}
void Start () {
gameManager = GameObject.Find ("GameController").GetComponent<Manager> ();
audio = GetComponent<AudioSource> ();
entered = false;
}
// Update is called once per frame
void Update () {
}
}

Unity3d delay in while true loop not working

I'm still working on the game and i ran into another problem, I'm trying to make an infinite loop which waits for a couple seconds every execute, i currently have:
StartScript.cs
using UnityEngine;
using System.Collections;
using ProgressBar;
public class StartScript : MonoBehaviour {
private ProgressBarBehaviour hunger;
private ProgressBarBehaviour stamina;
private ProgressRadialBehaviour health;
private ProgressBarBehaviour thirst;
public void OnGUI(){
this.hunger = GameObject.Find("hungerBar").GetComponent<ProgressBarBehaviour>();
this.stamina = GameObject.Find("staminaBar").GetComponent<ProgressBarBehaviour>();
this.health = GameObject.Find("healthBar").GetComponent<ProgressRadialBehaviour>();
this.thirst = GameObject.Find("thirstBar").GetComponent<ProgressBarBehaviour>();
this.health.Value = 100f;
this.stamina.Value = 100f;
StartCoroutine ("runHunger");
}
bool addBar(ProgressBarBehaviour target, float percentage){
Debug.Log (percentage);
if ((target.Value + percentage) <= 100f) {
target.IncrementValue (percentage);
return true;
}
return false;
}
bool damageBar(ProgressBarBehaviour target, float percentage){
if ((target.Value - percentage) >= 0f) {
target.DecrementValue (percentage);
return true;
}
return false;
}
bool addRadial(ProgressRadialBehaviour target, float percentage){
if ((target.Value + percentage) <= 100f) {
target.IncrementValue (percentage);
return true;
}
return false;
}
bool damageRadial(ProgressRadialBehaviour target, float percentage){
if ((target.Value - percentage) >= 0f) {
target.DecrementValue (percentage);
return true;
}
return false;
}
IEnumerator runHunger(){
while (true) {
yield return new WaitForSeconds(10f);
/*if (!this.addBar(this.hunger,5f)) {
this.damageRadial(this.health,3f);
}*/
Debug.Log("Time: "+Time.time);
}
}
IEnumerator runHealth(){
while (true) {
}
}
/*
IEnumerator runThirst(){
while (true) {
if (!this.thirst.Add (2)) {
this.health.Damage(8);
}
}
}*/
}
As you guys can see I'm trying to make a while(true){} loop with an yield return new WaitForSeconds(), the idea is that it runs the function in the loop every (in this test case) 10 seconds.
The first time executing works like a charm, it waits for 10 seconds, but after that it only waits for like 0.1 seconds and executes again.
I hope someone can help me with this problem.
There are too many wrong things I currently see in your code.
This:
IEnumerator runHealth(){
while (true) {
}
}
Change it to
IEnumerator runHealth(){
while (true) {
yield return null;
}
}
Also This:
IEnumerator runThirst(){
while (true) {
if (!this.thirst.Add (2)) {
this.health.Damage(8);
}
}
}
Change it to
IEnumerator runThirst(){
while (true) {
if (!this.thirst.Add (2)) {
this.health.Damage(8);
}
yield return null;
}
}
If you are gonna have while(true) in a coroutine, you must have yield return something or it will lock your program. Fix these problems first. It is good to have it just before the closing bracket of the while loop.
I am sure you did this in other scripts too.
Go over the rest of other scripts and do the same thing. Put yield return null inside each while loop in each coroutine function.
ANOTHER BIG MISTAKE:
Remove every code from OnGUI function and put it in Start function like below. The code is being called too several times per frame and that will lead to slow down as you will be creating many many coroutines that never stops due to your while loop in the coroutine function.Putting it in the Start function will call it once.
void Start()
{
this.hunger = GameObject.Find("hungerBar").GetComponent<ProgressBarBehaviour>();
this.stamina = GameObject.Find("staminaBar").GetComponent<ProgressBarBehaviour>();
this.health = GameObject.Find("healthBar").GetComponent<ProgressRadialBehaviour>();
this.thirst = GameObject.Find("thirstBar").GetComponent<ProgressBarBehaviour>();
this.health.Value = 100f;
this.stamina.Value = 100f;
StartCoroutine ("runHunger");
}
Call the StartCoroutine ("runHunger");
somewhere other than in OnGUI(). It will work then. Since everytime OnGUI is called it starts a new coroutine
Call the StartCoroutine in Start() or someplace else

Need Help Yielding a Coroutine

I'm running a coroutine where a GameObject's children's material.shaders are being manipulated. I just can't seem to figure out for the life of me how to make the coroutine wait until all of the materials are at the desired blend level, before going on to the next step. Everything's working just fine, I just need the coroutine not to continue past line 28 until all the materials are ChangedToWhite. Any thoughts?
public GameObject changeInto;
private bool coroutineStarted = false;
private Renderer[] renderersArray;
private List<Material> materialsList = new List<Material>();
private GameObject newForm;
void Start(){
renderersArray = this.gameObject.GetComponentsInChildren<Renderer>();
}
void Update(){
if(!coroutineStarted){
coroutineStarted = true;
StartCoroutine(Change());
}
}
private IEnumerator Change(){
foreach(Renderer renderer in renderersArray){
for(int i = 0; i < renderer.materials.Length; i++){
if(renderer.materials[i].shader.name == "Toon/Basic Blender"){
materialsList.Add(renderer.materials[i]);
}
}
}
foreach(Material material in materialsList){
StartCoroutine(ChangeToWhite(material));
}
animation.Play("Evolution");
newForm = Instantiate(changeInto, this.transform.position, this.transform.rotation) as GameObject;
yield return null;
}
private IEnumerator ChangeToWhite(Material mat){
float counter = mat.GetFloat("_Blend");
while(counter != 1f){
float increase = mat.GetFloat("_Blend") + 0.01f;
mat.SetFloat("_Blend", increase);
counter += increase;
yield return null;
}
}
this part:
foreach(Material material in materialsList){
StartCoroutine(ChangeToWhite(material));
}
has to contain the wait time of the change to white routine. In your case, it would be variable because you are not using Time.deltaTime which is HIGHLY encouraged by the way.
EDIT: here is the code you may want to use
foreach(Material material in materialsList){
StartCoroutine(ChangeToWhite(material));
yield return new WaitForSeconds(1);
}
private IEnumerator ChangeToWhite(Material mat){
float counter = mat.GetFloat("_Blend");
while(counter != 1f){ //the 1 here is the time to wait
float increase = mat.GetFloat("_Blend") + Time.deltaTime;
mat.SetFloat("_Blend", increase);
counter += increase;
yield return null;
}
}

Application.LoadLevel not working in Unity3d

I have a problem. Now I'm developing game in Unity when the condition is score 2. I want to wait for ten seconds before do the code change scene (Application.LoadLevel) (I'm using C# for develop)
But this code when score = 2 It will change to "scenea_5"
It can't wait for ten seconds
void OnTriggerEnter( Collider collider ){
if (collider.name == front_hztleft) {
audio.Play ();
}
if (collider.name == left_hztleft) {
audio.Play ();
score ++;
Debug.Log (string.Format (scoreSyntax, score));
endtime = DateTime.Now.ToString("HH:mm:ss");
InsertResult();
}
if (score == 2) {
StartCoroutine(Waiting());
Application.LoadLevel("scene_a5");
}
}
IEnumerator Waiting()
{
yield return new WaitForSeconds (10);
}
It can run and compile not without error.
Put the scene loading inside the Coroutine.
yield return new WaitForSeconds(10);
Application.LoadLevel("scene_a5");

Categories

Resources