Create infinite loop in Unity with delay - c#

I need to create a infinite loop in Unity without using the main thread? I saw some example, but it's not usefull:
while(true){
var aa;
debug.log("print.");
}
I want to add some delay like e.g. 2 seconds. If anybody knows the solution please help.

First define a Coroutines:
private IEnumerator InfiniteLoop()
{
WaitForSeconds waitTime = new WaitForSeconds(2);
while (true)
{
//var aa;
Debug.Log("print.");
yield return waitTime;
}
}
Then call it like that:
StartCoroutine(InfiniteLoop());
Added note:
If you happen to change Time.timeScale and don't want that to affect delay time, use:
yield return new WaitForSecondsRealtime(2);

Use this to create the loop;
private IEnumerator LoopFunction(float waitTime)
{
while (true)
{
Debug.Log("print.");
yield return new WaitForSeconds(waitTime);
//Second Log show passed waitTime (waitTime is float type value )
Debug.Log("print1.");
}
}
For calling the function, don't use Update() or FixedUpdate(), use something like Start() so you don't create infinite instances of the loop;
void Start()
{
StartCoroutine(LoopFunction(1));
}

Use coroutines..
//Call in your Method
StartCoroutine(LateStart(2.0f));
Then write coroutine like..
private IEnumerator LateStart(float waitTime)
{
yield return new WaitForSeconds(waitTime);
//After waitTime, you can use InvokeRepeating() for infinite loop infinite loop or you use a while(true) loop here
InvokeRepeating("YourRepeatingMethod", 0.0f, 1.0f);
}
Here is the documentation for InvokeRepeating():
https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html

Related

How can i make my caroutine run in every x seconds

I created a caroutine, and i want it to run in every x seconds. i tried using while loop but it didn't worked for this caroutine. can anybody please help ? thanks.
IEnumerator StartFire()
{
{
Firing = true;
animator.SetBool("isFiring", true);
yield return new WaitForSeconds(2);
Firing = false;
animator.SetBool("isFiring", false);
}
}
I think you would be better off using InvokeRepeating(string methodName, float time, float repeatRate). You only need to call it once and it will repeat.
Example:
void Start()
{
InvokeRepeating("myTask", 1.5f, 1.5f);
}
void myTask()
{
// Execute repetitive task.
}
If you want to stop it at any point you simply have to call: CancelInvoke(string methodName);.
Example:
void Update()
{
if (Input.GetKeyDown(KeyCode.S))
CancelInvoke();
}
This would put an end to the repetitive task.

Difference between while(true) in Coroutine then put it in the void Start() and not having while(true) but put it in the void Update()

I'm trying to make the game spawn enemies every random period of time so I'm using yield return new WaitForSeconds(...) in Coroutine and as the title says, I can use while(true) inside the Coroutine and call the Coroutine in the void Start() and the spawning thing works just fine. But when i removed the while(true) and call the Coroutine in void Update(), the enemies are spawned crazily without the delay. Why is this??
Because I guess you don't wait but rather start a new concurrent routine every frame so after the delay has passed once at the beginning you now get one call for each frame!
A Coroutine does not delay the outer code which starts it.
Either use e.g.
[SerializeField] private float delay;
private void Start()
{
StartCoroutine(Routine);
}
private IEnumerator Routine()
{
while(true)
{
yield return new WaitForSeconds(delay);
DoSomething();
}
}
or also directly
// If you make Start an IEnumerator then Unity automatically runs it as a Coroutine
private IEnumerator Start()
{
while(true)
{
yield return new WaitForSeconds(delay);
DoSomething();
}
}
Or if you want to go for Update the equivalent would require a counter like e.g.
private float timer;
private void Start()
{
timer = delay;
}
private void Udpate()
{
timer -= Time.deltaTime;
if(timer <= 0)
{
DoSomething();
timer = delay;
}
}
I wouldn't mix both things ;)
And personally I find Coroutines better to read and maintain most of the times.

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.

How to wait Coroutine callback is executed?

I want to wait StartCoroutine callback is executed.
Anyone knows how to do this?
public float getXXX() {
var result;
StartCoroutine(YYY((r) => result = r)); // how to wait this?
return result;
}
private IEnumerator YYY(System.Action<float> callback) {
LinkedList<float> list = new LinkedList<float>();
while(timeleft > 0) {
timeleft -= Time.deltaTime;
list.add(transform.position.magnitude);
yield return new WaitForSeconds (WAITSPAN);
}
callback(list.max());
yeild return true;
}
You can't and shouldn't try to wait or yield for a coroutine function to return from non coroutine function (getXXX function). It will block in that non coroutine function until this function returns preventing other Unity scripts to run.
To wait for a coroutine function(YYY) in the getXXX function, you must also make the function you are making the call and waiting from in a coroutine function. In this case this is theYYY function, so that should be a corutine function too then you can yield it:
public IEnumerator getXXX()
{
float result = 0f;
yield return StartCoroutine(YYY((r) => result = r)); // how to wait this?
//Use the result variable
Debug.Log(result);
}
OR
If you don't want to make the getXXX function a a coroutine function then don't try to wait there. You can still use the result from the YYY coroutine function but don't try to return the result. Just use it to do whatever you want to do in that function:
public void doSomethingXXX()
{
StartCoroutine(YYY((result) =>
{
//Do something with the result variable
Debug.Log(result);
}));
}
The idea of using coroutine is to be able to do something over multiple frames. The void function will just do that in one frame. You can't yield/wait in a void or non IEnumerator/coroutine functio.
You can only wait inside a coroutine. To do this, your getXXX() method should also be a coroutine. Something like this:
public float someOtherMethod()
{
float result;
StartCoroutine(getXXX(out result));
return result;
}
IEnumerator getXXX(out float result)
{
//more code here...
yield return StartCoroutine(YYY((r) => result = r));
//more code here...
}
IEnumerator YYY(System.Action<float> callback)
{
//your logic here...
}
I found something you may be able to call the class and use the methods inside this code and modify to use this is very similar to your code but less complex:
using UnityEngine;
using System.Collections;
public class WaitForSecondsExample : MonoBehaviour
{
void Start()
{
StartCoroutine(Example());
}
IEnumerator Example()
{
print(Time.time);
yield return new WaitForSeconds(5);
print(Time.time);
}
}
this code was taken from: https://docs.unity3d.com/ScriptReference/WaitForSeconds.html
from there examples
The 5 is the amount of time it will wait unless you want to do this dynamically based upon something. Depends on what you want to do. However notice how they are calling the method Example() inside the StartCoroutine(Example()) and which will go to the IEnumerator Example() and within there you have WaitForSeconds(5); this will make the StartCoroutine wait for 5 seconds. This can be hardcoded or made to wait dynamically by calling another method within that class from within IEnumerator Example() this is just one of many ways you can attack this. Again depends on what you want to do. Actually, you might even be better off making this into a method that passes a value in to the method each time something like
IEnumerator Example(float flSeconds)
{
print(Time.time);
yield return new WaitForSeconds(flSeconds);
print(Time.time);
}
this way you can pass what is in your
LinkedList list = new LinkedList();
Every time

Why does this Coroutine only run once?

"Something" is only printed once...
IEnumerator printSomething;
void Start () {
printSomething = PrintSomething();
StartCoroutine (printSomething);
}
IEnumerator PrintSomething () {
print ("Something");
yield return null;
StartCoroutine (printSomething);
}
The misstake in your approach is that you save the enumerator. A enumerator is already "enumerating" therefore giving the enumerator to the StartCoroutine-method twice basically results in direct exit of the coroutine as the enumerator has been used before. Starting the coroutine again can be done by calling the function again.
StartCoroutine(PrintSomething());
But instead of starting the coroutine over and over again try to use a loop inside instead.
while (true)
{
print("something");
yield return null;
}
This is better as internal handling of the coroutine and its overhead is unknown.
Try co-routine's name instead of a pointer. Or co-routine itself.
IEnumerator PrintSomething ()
{
print ("Something");
yield return null;
StartCoroutine ("PrintSomething");
}
Or
IEnumerator PrintSomething ()
{
print ("Something");
yield return null;
StartCoroutine (this.PrintSomething());
}
I ran into this exact same issue, Felix K. is right in that it assumes the IEnumerator has already been run and just immediately returns. My solution was to pass the function itself so that we generate a new IEnumerator each time it's called. I hope this helps someone else!
public IEnumerator LoopAction(Func<IEnumerator> stateAction)
{
while(true)
{
yield return stateAction.Invoke();
}
}
public Coroutine PlayAction(Func<IEnumerator> stateAction, bool loop = false)
{
Coroutine action;
if(loop)
{
//If want to loop, pass function call
action = StartCoroutine(LoopAction(stateAction));
}
else
{
//if want to call normally, get IEnumerator from function
action = StartCoroutine(stateAction.Invoke());
}
return action;
}

Categories