I currently have such a piece of code in panelmanager where I open certain panels (I have a similar piece of code for closing panels). But I wanted to make sure that after pressing the button, there was no instant switching between panels, but with a delay. I read that it can be done with coroutines, but I haven't succeeded yet, since I'm probably using it incorrectly. Please tell me how I can implement such a delay correctly, I'm a complete noob..
public void OpenPanel(string name)
{
switch (name)
{
case nameof(MainMenu):
StartCoroutine(CoroutineSample());
MainMenuPanel.gameObject.SetActive(true);
break;
case nameof(LevelsPanel):
StartCoroutine(CoroutineSample());
LevelsPanel1.gameObject.SetActive(true);
break;
}
}
private void Start()
{
StartCoroutine(CoroutineSample());
}
private IEnumerator CoroutineSample()
{
yield return new WaitForSeconds(2);
}
You have to call the
MainMenuPanel.gameObject.SetActive(true);
inside the coroutine after the yield return line.
As it is in your code the StartCoroutine just starts the coroutine and then continues immediately with the next line. So it won't wait. And the WaitForSeconds call doesn't help anything because after the 2 seconds the coroutine doesn't do anything.
Related
I'm creating a Pop up menu Option in Unity. Now my Problem here is that the coroutine i made in void update is being called so many times. What i mean by that is on my Unity Console the Debug.Logs are incrementing . It should not right because its already coroutine. Could some help me understand more coroutine and help me solve my little problem .
Here is my code:
[SerializeField]
GameObject Option;
[SerializeField]
Button btn,btn2;
[SerializeField]
GameObject open, close;
[SerializeField]
GameObject[] opt;
bool startFinding = false;
void Start()
{
Option.SetActive(false);
Button popUp = btn.GetComponent<Button>();
Button popUp2 = btn2.GetComponent<Button>();
popUp.onClick.AddListener(PopUpOption);
popUp2.onClick.AddListener(ClosePopUp);
}
void Update()
{
if (startFinding)
{
StartCoroutine(GameOptions());
}
}
IEnumerator GameOptions()
{
//Get All the tags
opt = GameObject.FindGameObjectsWithTag("MobileOptions");
if (opt[0].GetComponent<Toggle>().isOn == true && opt[1].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Disable first the check box then choose only 1 option between" + "'rendering'"+ "and" + "'livestreaming'");
}
//Livestreaming
if (opt[0].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Livestreaming Activate");
} else
{
Debug.Log("Livestreaming Deactivate");
}
//Rendering
if (opt[1].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Rendering Activate");
} else
{
Debug.Log("Rendering Deactivate");
}
//Fog
if (opt[2].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Fog Activated");
} else
{
Debug.Log("Fog Deactivated");
}
//Camera Effect
if (opt[3].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Camera Effect Activated");
} else {
Debug.Log("Camera Effect Deactivated");
}
yield return null;
}
void PopUpOption()
{
startFinding = true;
//Disable The Mobile Option Button
open.SetActive(false);
//Enable the Close Option Button
close.SetActive(true);
//activate the Mobile Options
Option.SetActive(true);
}
void ClosePopUp()
{
startFinding = false;
//eanble the mobile option button
open.SetActive(true);
//disable the close option button
close.SetActive(false);
//deactivate the Mobile Option
Option.SetActive(false);
}
Here is how coroutines work:
Let's say I have a couroutine function called MyRoutine (in your case, you called it GameOptions)
private IEnumerator MyRoutine()
Then, anywhere in my code, calling
StartCoroutine(MyRoutine));
Is going to simply call MyRoutine like any usual method. So if you call it in update, it will be called all the time, as any method would. This is not what you want. What make coroutines special is that you can use the yield keyword in them. There are many ways to use it but the most used (and simple) one is to do yield return null
yield return null means "Stop this coroutine, but resume the execution on next frame". You don't need to call any other function (certainly not StartCoroutine). The execution will resume next frame.
To go back to what you posted in your question, you wrote yield return null at the end. So your method is executing, and just at the end, stops and resumes next frame, but since there is nothing left to do, it exits on the next frame.
A typical way to use coroutines is to have the yield return null in a while loop, so when it resumes, it continues the loop. Here is an example that do it
private IEnumerator MyRoutine()
{
while(running) //running is a member bool that you could set to false to exit
{
// Do all the stuff you want to do in ONE frame
// ...
yield return null;
}
}
Typically, the StartCoroutine would be called in the Start() function, or later when an event is triggered.
If you want to know more about coroutine, or check that you understood them properly, check out this page: https://docs.unity3d.com/Manual/Coroutines.html
or this video https://unity3d.com/learn/tutorials/topics/scripting/coroutines
// Edit: quickly present one useful option
In the snippet above, the while loop is very similar to the Update function (the inside of the loop is executed each frame). One nice option is to replace
yield return null
by
yield return new WaitForSeconds(waitTime)
where waitTime is a the time you want to wait before resuming, in seconds
// End of edit
Do not use StartCoroutine() in the Update method. Call it in another method and use a while loop inside your coroutine function if needed. Just control your StartCoroutine() outside of Update method
Update is called every frame, if your condition is ever true, you launch your coroutine every frame.
Just set down your flag to only join 1 time.
void Update()
{
if (startFinding)
{
startFinding = false;
StartCoroutine(GameOptions());
}
}
I don't think I fully understand coroutines, it doesn't work the way I wanted, so I need help.
I've got a memory game (Simon like) that consist of 4 squares that randomly switches on and off. After square switches on/off it should take a little break, before switching next button, which my program does not seem to do so. For the switching process I use blink*Colorname coroutine which is following:
foreach (int color in pattern)
{
switch (color)
{
case 0:
StartCoroutine (blinkGreen (blinkSeconds));
break;
case 1:
StartCoroutine (blinkRed (blinkSeconds));
break;
default:
break;
}
}
//to do: pause function between button blinks
IEnumerator blinkGreen (float seconds)
{
greenImg.color = Color.white;
yield return new WaitForSeconds (seconds);
greenImg.color = Color.green;
}
I've tried using waitforseconds at 2 places to achieve my goal: First, at blink*Color as following:
IEnumerator blinkGreen (float seconds)
{
greenImg.color = Color.white;
yield return new WaitForSeconds (seconds);
greenImg.color = Color.green;
yield return new WaitForSeconds (seconds);
}
Second, after the the loop, under //to do: pause function between button blinks by calling another coroutine:
StartCoroutine(waitfornexttransition(5.0f));
IEnumerator waitfornexttransition (float second)
{
yield return new WaitForSeconds (second);
}
Am I missing something? All suggestions and helps are appreciated. Thanks!
Unity does not wait for the completion of the started coroutine to continue code execution. If you type (psuedocode):
print "1"
start coroutine that prints "2" after a second
print "3"
the output will be: "1", "3", "2".
If you put all your code in one coroutine, everything will run sequentially as expected.
Well, since it's all in a foreach loop anyway and you're starting a new couroutine for each iteration, then basically all the coroutines are starting at the same time within the same frame. They will then all run concurrently regardless of what the rest of your code is doing, blinking all lights simultaneously.
If you want your code to wait for a certain amount of time between executions, then make a timer and only blink the lights when the timer hits 0. A basic example would be:
private float timer;
void Start()
{
timer = 2f; // Waiting 2 seconds between blinks
}
void Update()
{
timer -= Time.deltaTime; // Consistently decrement timer independently of frame rate
if (timer <= 0)
{
blinkLight();
timer = 2f;
}
}
void blinkLight()
{
// Light blinking logic goes here.
}
Your foreach loop should also be inside a coroutine and you can put a yield return new WaitForSeconds (seconds) before each iteration.
This is the simple code inside of update. sadly it is not working for me. Even I have set Wrap Mode to clamp Forever.
if (trainGO.GetComponent<Animation>()["Start"].time >= trainGO.GetComponent<Animation>()["Start"].length)
{
blackTrain.SetActive(true);
redTrain.SetActive(false);
}
How can I check that animation clip have finished so that I can do some work? I don't want to use WaitForSeconds method, because I am working on Time and each time a new animation will be played,
Basically like this ...
PlayAnimation(); // Whatever you are calling or using for animation
StartCoroutine(WaitForAnimation(animation));
private IEnumerator WaitForAnimation ( Animation animation )
{
while(animation.isPlaying)
yield return null;
// at this point, the animation has completed
// so at this point, do whatever you wish...
Debug.Log("Animation completed");
}
You can trivially google thousands of QA about this example, http://answers.unity3d.com/answers/37440/view.html
If you do not fully understand coroutines in Unity, step back and review any of the some 8,000 full beginner tutorials and QA regarding "coroutines in Unity" on the web.
This is really the way to check an animation is finished in Unity - it's a standard technique and there's really no alternative.
You mention you want to do something in Update() ... you could possibly do something like this:
private Animation trainGoAnime=ation;
public void Update()
{
if ( trainGoAnimation.isPlaying() )
{
Debug.Log("~ the animation is still playing");
return;
}
else
{
Debug.Log("~ not playing, proceed normally...");
}
}
I strongly encourage you to just do it with a coroutine; you'll end up "in knots" if you try to "always use Update". Hope it helps.
FYI you also mention "actually the scenario is: at specified time (Time/clock separately running). I have to play an animation thats why i am using update to check the time and run animation accordingly"
This is very easy to do, say you want to run the animation 3.721 seconds from now...
private float whenToRunAnimation = 3.721;
it's this easy!
// run horse anime, with pre- and post- code
Invoke( "StartHorseAnimation", whenToRunAnimation )
private void StartHorseAnimation()
{
Debug.Log("starting horse animation coroutine...");
StartCoroutine(_horseAnimationStuff());
}
private IENumerator _horseAnimationStuff()
{
horseA.Play();
Debug.Log("HORSE ANIME BEGINS");
while(horseA.isPlaying)yield return null;
Debug.Log("HORSE ANIME ENDS");
... do other code here when horse anime ends ...
... do other code here when horse anime ends ...
... do other code here when horse anime ends ...
}
I searched for using WaitforSeconds and used as it was mentioned(using a return type of IEnumeration and using coroutines instead of update). but it did not work. Initially it showed Waitfor Seconds and IEnumerator were not "present in the current context". I had to r-install unity to get it fixed but this problem still remains. The following is my code. Am I using WaitforSeconds in correct way? Is it the 'if' code block that ruin my complete work(I mean have I used it in wrong place)?
using UnityEngine;
using System.Collections;
public class EnemyCreeator : MonoBehaviour
{
public float xmin,xmax,zmin,zmax;
public GameObject enemy;
public float spawnWait,StartWait,waveWait;
public int turretCount;
public int enemyCount;
int i=0;
void Update()
{
StartCoroutine (TurretWaves());
}
IEnumerator TurretWaves()
{
while (true)
{
Vector3 pos=new Vector3(Random.Range (xmin,xmax),0.5f,Random.Range(zmin,zmax));
yield return new WaitForSeconds(StartWait);
while(i<turretCount)
{
//Debug.Log ("The vaue of game time in spawnwaiting is: "+Time.time);
Instantiate (enemy, pos, Quaternion.identity);
enemyCount++;
i++;
//Debug.Log ("value of i in loop is: "+i);
//Debug.Log ("The vaue of game time is: "+Time.time);
yield return new WaitForSeconds (spawnWait);
}
//Debug.Log("cHECKing Before WAVE WAIT(value of time )is: "+Time.time);
yield return new WaitForSeconds(waveWait);
if(i>=turretCount)
{
i=0;
}
//Debug.Log("cHECKing AFTER WAVE WAIT and value of time is: "+Time.time);
//Debug.Log ("value of i outside the while loop is: "+i);
}
}
}
The code needs to wait until the spawnWait before spawning each turret and wait till the wavewait before spawning the next wave. even though the Startwait works fine, I still am unable to find the problem with others...
Thanks in advance.
Your code is correct, except
void Update()
{
StartCoroutine (TurretWaves());
}
By doing this, you are creating a new coroutine in every frame. Therefore, after Startwait seconds, each running coroutine will spawn an enemy in the following frames, making the overall script works incorrectly. But, in fact, each coroutine works as you expected.
To solve your problem, change
void Update()
to
void Awake()
or
void Start()
such that only one coroutine is started. It should work as expected.
Edit #1:
I think you misunderstood how coroutine works in Unity, because it seems that you are using Update to manually execute the code inside coroutine frame by frame.
When a coroutine is started by StartCoroutine, a "task" will be registered inside Unity. This task is like another thread (except it is still in the main thread). Unity will execute the code inside this task automatically. When it meets a yield statement, it pauses and waits for the statement to end. (Internally, it checks the state continuously and determine when it should resume.) For example, yield return new WaitForSeconds(n) will make Unity temporarily stops executing the code inside the coroutine for n seconds. And after that, it will keep going on.
The task will be deregistered until no more code should be executed (in your case, it never ends because of the while-loop), or the game object that started the coroutine is destroyed or deactivated.
I have a function that calls a first dialogue box, then needs to wait for the user to press the space bar before it displays the second dialogue box. I can accomplish this easily with a coroutine, by yielding within a while loop as follows:
message.PlayMessage();
while (Input.GetKeyDown (KeyCode.Space) == false) {
yield return null;
}
message.PlayMessage(2);
My question is: is this a weird solution? I feel like there might be an actual function for this, and I fear that maybe this is eating up a lot of system resources for no reason.
It'll be called once in a frame, not expensive.
And making Wait function is more convenient.
IEnumerator MyMethod() {
message.PlayMessage();
yield return StartCoroutine(WaitForKeyDown(KeyCode.Space));
message.PlayMessage(2);
}
IEnumerator WaitForKeyDown(KeyCode keyCode)
{
while (!Input.GetKeyDown(keyCode))
yield return null;
}
And call.
StartCoroutine(MyMethod());