- Home /
Custom fixed update cycle?
I want to call an event every X seconds where X is variable. I've looked into InvokeRepeating but it seems like I can't modify the rate after calling the method. I think the best way would be to use a repeating coroutine. Does StartCoroutine_Auto do this? I'm a bit confused as to how it works. If anyone can show me an example, I'd really appreciate it. Thanks.
Answer by gfoot · Feb 24, 2015 at 02:22 AM
You can make a coroutine that waits arbitrary lengths of time between processing:
private IEnumerator Coro()
{
while (true)
{
DoStuff();
yield return new WaitForSeconds(HowLongShouldIWait());
}
}
public void Start()
{
StartCoroutine(Coro());
}
Not sure if it helps. It is equivalent to, but somewhat tidier than, summing up a float in your Update method and using that to decide whether it's time to "do stuff" yet or not. I prefer it a lot, though, especially compared to InvokeRepeating.
If you want more accurate callbacks than once per Update then you need to start your own thread. There is still always a limit to the extent to which you can get called at specific times under a multitasking operating system, let alone within C# code hosted by Unity, but I believe using a separate thread is the best you can do. There are also ways to ask Unity to call your coroutine at different points in the frame, e.g. during the next FixedUpdate or just before presenting a frame to the viewport, but these won't really help you either.
Using a separate thread, then, you just loop forever as above but call "Thread.sleep(XXX)" instead of "yield return ..." to wait for a while before continuing processing. It is not entirely accurate, so you probably want to combine it with a Stopwatch so that although you can't choose exactly when your code runs, you do know exactly when it runs.
Also beware of caveats using threads with Unity. I don't know for sure the status now, but in the past it has caused instability, especially if you don't proactively kill the threads when leaving play mode.
Just to be clear, coroutines are not threads. Update and coroutine both run on the main thread. Should avoid threading if possible.
The WaitForSeconds() allows me to very easily adjust the rate of the cycle too. Thanks!
@jessespike Does this mean the coroutine is dependent on the frame rate?
you can use WaitForEndOfFrame(); which will make it execute every frame. You just need to compensate the difference in time between frames. e.g., myPosition.x += speed * Time.deltaTime;
Sure, coroutines are not threads, they just look a bit like them. I mentioned threads as a separate option that can provide more accurate scheduling, in case you need a higher frequency callback than once per frame.
@gfoot Nice to see threads mentioned :) I see a lot of people on here saying "don't use threads in Unity" which is utter rot. Sure, one's limited in what one can do in threads but they work fine and when they're the right thing to use then one should definitely use them. They're just not often the right thing to use.
Answer by Jessespike · Feb 24, 2015 at 02:23 AM
Coroutine can easily do this.
http://docs.unity3d.com/ScriptReference/Coroutine.html
http://unity3d.com/learn/tutorials/modules/intermediate/scripting/coroutines
//A coroutine that loops and yields for X amount of time
public float secondsToWait = 2f;
public bool isRunningCoroutine = true;
private float elapsedTime = 0f;
void Start()
{
StartCoroutine( MyLoop() );
}
IEnumerator MyLoop()
{
while (isRunningCoroutine)
{
yield return new WaitForSeconds(secondsToWait);
Debug.Log("Ding, firing event");
}
yield return null;
}
If you want to update something else every frame that is based on deltaTime, I would put that in the update loop or in a seperate coroutine.
Answer by maccabbe · Feb 24, 2015 at 12:37 AM
If you want an event to be called every x seconds then you can use Update like following
using UnityEngine;
using System.Collections;
public class NewBehaviourScript : MonoBehaviour {
float time=0;
public float x;
void Update() {
time+=Time.deltaTime;
while(time>x) {
time-=x;
Event();
}
}
void Event() {
Debug.Log(x+" seconds have passed");
}
}
Edit: I would really encourage you to use Update or FixedUpdate over InvokeRepeating. However I think the following should be what you wanted.
using UnityEngine;
using System.Collections;
public class NewBehaviourScript : MonoBehaviour {
private float _x;
private float lastInvokeTime;
public float x {
get {
return _x;
}
set {
_x=value;
float time=Mathf.Max(value-(Time.realtimeSinceStartup-lastInvokeTime), 0);
CancelInvoke();
InvokeRepeating("Event", time, value);
Debug.Log("set x to "+value+" and first wait time to "+time);
}
}
void Event() {
Debug.Log(x+" seconds have passed");
lastInvokeTime=Time.realtimeSinceStartup;
}
public float newX=1;
void Update() {
if(newX!=x) {
x=newX;
}
}
}
Ah, thanks, but I'm looking for a little more accuracy and my game runs independent of frame rate. If the FPS were to drop to 5, I'd be in big trouble.
Thanks for the update but how do I change the rate of InvokeRepeating after I call it?
The rate of InvokeRepeating is changed when x is changed. This is done by canceling Invoke then starting it up again with x as the time between invokes and x-(Time.realtimeSinceStartup-lastInvokeTime) as the time until next invoke.
Answer by ekeldevious · Feb 24, 2015 at 03:37 AM
c#
public void TimerFunction(int x){
invoke("targetfunction",x);
invoke("TimerFunction",x)
}
this is what I would do.
just change x at any time to space the function as desired
Answer by EggQuiz857 · Feb 24, 2015 at 02:32 AM
I would do some sort of one script activates it self. do it like this
function Start()
{
wait();
}
function wait()
{
yield WaitForSeconds(5);
doSomthing()
}
function doSomthing()
{
//do what you want in here
}
simple