Code not starting/stopping animation - c#

Logically this code looks fine to me but is not working as intended when i run the code the animation does not activate or deactivate. I put the qwerty int in there for testing purposes.
void WaitingForPipe () {
qwerty = 1;
PipeEntry.GetComponent <Animator>().enabled=true;
Wait ();
qwerty = 2;
//yield return new WaitForSeconds(2);
PipeEntry.GetComponent<Animator>().enabled=false;
qwerty = 3;
//GameObject.Find("FPSController").GetComponent("FirstPersonController").enabled=true;
}
IEnumerator Wait()
{
//This is a coroutine
//Debug.Log("Start Wait() function. The time is: "+Time.time);
//Debug.Log( "Float duration = "+duration);
yield return new WaitForSeconds(2); //Wait
//Debug.Log("End Wait() function and the time is: "+Time.time);
}

Coroutines in C# aren't methods that you run by just calling them, that only works in UnityScript. In order to start a Coroutine in C#, you must call StartCoroutine(YourCoroutine()); on a MonoBehaviour instance.
Your code would work if it looked like this:
IEnumerator WaitingForPipe()
{
PipeEntry.GetComponent <Animator>().enabled=true;
yield return new WaitForSeconds(2);
PipeEntry.GetComponent<Animator>().enabled=false;
}
and you have to start the Coroutine by using StartCoroutine(WaitingForPipe()); from somewhere.
You can read more about Coroutines here in the official documentation: https://docs.unity3d.com/Manual/Coroutines.html

It looks like you want to wait 2 seconds before running Wait ();. If that's true then you must yield your your Wait (); function.
Change
Wait ();
to
yield return Wait();
and you must change your WaitingForPipe function to a coroutine function too so that you can yield inside it. void WaitingForPipe () should be IEnumerator WaitingForPipe ()
Code not starting/stopping animation
There is no code in your question that is starting or stopping the animation, you do not and should not have to enable/disable the Animator in order to play or stop it. That's wrong. Remove your PipeEntry.GetComponent <Animator>().enabled=true/false code.
This the proper way to play/stop animation:
Play:
string stateName = "Run";
Animator anim = GetComponent<Animator>();
anim.Play(stateName);
Stop:
anim.StopPlayback();

Related

Is there a reason why only one coroutine works in my code? [duplicate]

This question already has an answer here:
Wait for coroutine to finish [duplicate]
(1 answer)
Closed 3 years ago.
I have an enemy which currently attacks all the time, with no stopping in between attacks. I want to make enemy wait amount of time before he attacks again to simulate enemy "resting" and giving player a chance to attack him while not attacking. I needed coroutine to finish my animation playing so it can go to idle animation while waiting:
IEnumerator Attacking()
{
yield return new WaitForSeconds(animLenght);
isAttacking = false;
}
I have made another coroutine to wait for a second before enabling attacking again, but it doesen't work. Enemy attacks without brakes, like coroutine doesen't work:
IEnumerator WaitForAttack()
{
yield return new WaitForSeconds(1);
}
I have put WaitForAttack() coroutine in my Attack function:
private void Attack()
{
StartCoroutine(WaitForAttack());
isAttacking = true;
StartCoroutine(Attacking());
}
I would like to know what I'm doing wrong with coroutines, as I have just started using them, and this problem is troubling me for a very long time now.
The documentation for MonoBehaviour.StartCoroutine says
StartCoroutine function always returns immediately
So the Attack method will not wait after calling StartCoroutine(WaitForAttack()); and set isAttacking = true; immediately. Instead, set isAttacking in the coroutine itself. Also do both in the same coroutine to ensure the operations are performed in sequence. Otherwise both coroutines will run at the same time in parallel.
IEnumerator WaitAndAttack()
{
yield return new WaitForSeconds(1);
isAttacking = true;
yield return new WaitForSeconds(animLenght);
isAttacking = false;
}
private void Attack()
{
StartCoroutine(WaitAndAttack());
}
Coroutines can only suspend themselves (yield return ____) and not the method or object calling them. They are not the same as a synchronous method. When a coroutine is invoked and returns back to the parent method, that parent method will continue in the same frame.
In your method, you call "WaitForAttack()" and "Attacking()" from the same method on the same frame. "WaitForAttack()" literally does nothing.
Here's an example of a Coroutine that runs 5 separate times, once every second after it is called. Note that the var waitForSeconds is initialized once rather than every time I yield control back to the main thread. This is a minor optimization, but is considered best practice.
class TimedCoroutine : MonoBehaviour
{
var waitForSeconds = new WaitForSeconds(1);
IEnumerator CountdownToAction()
{
int countdown = 5;
while(countdown >= 0)
{
print(countdown--);
yield return waitForSeconds;
}
//Perform action here
}
}

Coroutine function stops at WaitForSeconds

My coroutine funtion works only one time. The _isPaused variable is true. Can't find out what I did wrong.
IEnumerator Movement()
{
while (_isPaused) // I checked, it's true
{
Debug.Log("Some action");
yield return new WaitForSeconds(0.8f);
}
}
void Update ()
{
if (Input.GetKeyDown(KeyCode.P))
{
StartCoroutine(Movement());
}
}
Here is the complete code:
This is your code:
while (_isPaused)
{
Debug.Log("Some action");
yield return new WaitForSeconds(0.8f);
}
Your while loop will pause when it reaches the WaitForSeconds. It will pause because you're setting Time.timeScale to 0 somewhere in the code when "P" is pressed. This behavior is normal. It's made so that you can pause your coroutine functions with Time.timeScale. It will upause and continue to run as soon as you set Time.timeScale back to 1.
If you do not want Time.timeScale to pause your coroutine function when you're waiting with WaitForSeconds then use WaitForSecondsRealtime instead of WaitForSeconds.
while (_isPaused)
{
Debug.Log("Some action");
yield return new WaitForSecondsRealtime(0.8f);
}
This is because WaitForSeconds is implemented with Time.deltaTime or a similar property which also becomes 0 when Time.timeScale is set to 0 causing the timer to pause while WaitForSecondsRealtime is implemented with Time.realtimeSinceStartup which is not affected by Time.timeScale at all.
Without the whole code of the program, it is hard to tell what is wrong. I suggest you test the coroutine using true
IEnumerator Movement()
{
while (true)
{
Debug.Log("Some action");
yield return new WaitForSeconds(0.8f);
}
}
instead of using the _isPause field.

Reset a repeating Coroutine delay

I'm using a Coroutine to set up a repeating delay as follows.
In my Awake I have
StartCoroutine(RandomMove());
And then further down
IEnumerator RandomMove()
{
while (true)
{
// print(Time.time);
yield return new WaitForSeconds(foo);
// print(Time.time);
}
}
where 'foo' is a random float value that I change with every iteration.
Lets say foo is 10 seconds and part way thru the delay I need to reset the delay so it starts 'counting down' from 10 again.
How would I accomplish this? Should I use a Timer instead?
I don't like either of the two existing answers. Here's what I'd do:
Kill and Restart the coroutine:
We'll start with this part of the killer_mech's answer:
Coroutine myCoroutine;
void Awake() {
myCoroutine = StartCoroutine(RandomMove());
}
But we're going to handle the rest differently. killer_mech never did anything with the reference, other than to keep ovewriting it.
Here's what we're doing instead:
public void resetRandomMove() {
StopCoroutine(myCoroutine);
myCoroutine = StartCoroutine(RandomMove());
}
Call this any time you need to reset it.
I would suggest you first store Coroutine in a variable.
Coroutine myCoroutine;
void Awake()
{
myCoroutine = StartCoroutine(RandomMove());
}
and change the coroutine function as
IEnumerator RandomMove()
{
// print(Time.time);
yield return new WaitForSeconds(foo);
// print(Time.time);
// Call your new coroutine here
myCoroutine = StartCoroutine(RandomMove());
}
this way you will have a coroutine variable for every iteration. If you need to stop the coroutine just say :
StopCoroutine(myCoroutine);
in your function at required time.This will allow you to stop a coroutine in middle before the end of countdown. Also at the end of coroutine it will start new coroutine with updated reference After finishing your task just call back again with
myCoroutine = StartCoroutine(RandomMove());
Hope this resolves your problem. Yes you can do it with timer also with a boolean flag the same thing but I think using coroutine is much simpler.
.
Hmmm it could something like this also . Just for my own .
void Start() {
StartCoroutine(RepeatingFunction());
}
IEnumerator RepeatingFunction () {
yield return new WaitForSeconds(repeatTime);
StartCoroutine( RepeatingFunction() );
}
As i understand the question. InvokeRepeating() is also a choice.
Maybe it is because you are each frame waiting for new assigned seconds?
Why don't you make the random before yielding the wait, and store the CustomYieldInstruction instead of yielding a new instance, since it disposes the one that was before, that creates memory problems. You won't notice that if you yield return a WaitForSeconds with a constant value, but maybe with a random one creates ambiguity and resets the timer (see this Unity's optimization page, on the Coroutine's section). A quick example:
public float foo;
public float min;
public float max;
void Awake()
{
StartCoroutine(Countdown());
}
IEnumerator Countdown()
{
while(true)
{
foo = Random.Range(min, max);
WaitForSeconds wait = new WaitForSeconds(foo);
yield return wait;
}
}
Also, #ryeMoss's solution seems a good one, to stop and restart the coroutine if 'foo' changes.
Hope it helps.

Unity coroutine does not work

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.

C# : How couroutine stop the loop

I want to understand the concept of coroutine i don't know why code stop at when it print 1,2,3.But in this code loop should run 30 times and print value 1 to 30.
public class NewCore : MonoBehaviour
{
void Start ()
{
StartCoroutine (MyCoroutine (0.52f));
StartCoroutine (CoroutineKiller (2f));
}
IEnumerator MyCoroutine (float delay)
{
int value = 0;
while (value < 30)
{
yield return new WaitForSeconds (delay);//wait
value ++;
print (value);
}
StartCoroutine (MyCoroutine (delay));
}
IEnumerator CoroutineKiller (float delay)
{
yield return new WaitForSeconds (delay);
StopAllCoroutines ();
}
}
You are printing values from 1 - 30 with delay of 0.52sec, but after 2 seconds you stop doing so (you call StopAllCoroutines). Which is why you are only seeing 1, 2 and 3 printed out.
Try to comment out StartCoroutine (CoroutineKiller (2f));, or give it more delay to see how it controls the flow stop.
Coroutines are similar to threads - although not really - but yes in the sense that calling StartCoroutine does not block the code.
So when you're doing this:
void Start ()
{
StartCoroutine (MyCoroutine (0.52f));
StartCoroutine (CoroutineKiller (2f));
}
It will actually start both coroutines and execute them step by step, side by side, on each Update call.
If you wish to execute the coroutines one after another, try the following:
void Start()
{
StartCoroutine(F());
}
IEnumerator F()
{
// by yielding the Coroutine, it will block the execution of
// this coroutine (F) until MyCoroutine finishes
yield return StartCoroutine(MyCoroutine(1.52f));
// This is no longer doing much, really.
yield return StartCoroutine(Killer(2f));
print("done");
}
You can read more about the Execution Order which also includes information about coroutines and how the yield system actually works.
It seems you using StopCoroutine(MyCoroutine()); in CoroutineKiller and it stops coroutine after 2 seconds. (your parameter is 2f. And I think you used it for WaitForSeconds). So your MyCoroutine function working just 3 delay ( 0.52 + 0.52 + 0.52 =1.56) and before fourth delay ends, you stop this coroutine and you get just 1,2,3 printed.

Categories