I have a particular function which I am calling in Update function. For the first ten seconds, the function should get called in Update function, then it should get disabled for next two seconds, then again enable it for next ten seconds. This cycle should keep repeating? How can I execute it?
4 Answers
Alternatively, there is one-liner using modulus:
void Update() { //if you want it loop from specific start time rather than from start of the game, //subtract said time value from Time.time argument value if(Mathf.Repeat(Time.time, execDuration + sleepDuration) < execDuration) executeFunction(); } - 1\$\begingroup\$ Is there a difference between
Repeat()and%(modulus)? The documentation says "this is similar to the modulo operator but it works with floating point numbers", but modulus works with floats... \$\endgroup\$BlueRaja - Danny Pflughoeft– BlueRaja - Danny Pflughoeft2017-06-07 15:19:24 +00:00Commented Jun 7, 2017 at 15:19 - 1\$\begingroup\$ @BlueRaja-DannyPflughoeft yes and no - as far as I know in various languages the
%operator often act weirdly - either doesn't work with floating point numbers, gives unexpected or outright incorrect results for modulus operation in its math meaning (reflecting hardware nature of the operation on integers).Repeat()was chosen just as a safer option to avoid necessity of looking up exact implementation of%operator in C#/mono. \$\endgroup\$wondra– wondra2017-06-07 15:30:37 +00:00Commented Jun 7, 2017 at 15:30 - 3\$\begingroup\$ I thought every language implemented the IEEE 754 standard, but TIL the standard's "modulo" definition is unintuitive so almost no languages implement it. \$\endgroup\$BlueRaja - Danny Pflughoeft– BlueRaja - Danny Pflughoeft2017-06-07 16:27:48 +00:00Commented Jun 7, 2017 at 16:27
- \$\begingroup\$ It would be better if you implemented the comment over code as another code block and titled them accordingly. \$\endgroup\$starikcetin– starikcetin2017-06-11 11:11:44 +00:00Commented Jun 11, 2017 at 11:11
I haven't tested the following code, but you will get the idea :
public float wakeUpDuration = 10.0f ; public float sleepDuration = 2.0f; private bool callFunction = true ; private float time = 0 ; void Update() { time += Time.deltaTime; if( callFunction ) { if( time >= wakeUpDuration ) { callFunction = false; time = 0 ; } else { foo(); // Your function } } if( !callFunction && time >= sleepDuration ) { callFunction = true; time = 0 ; } } - \$\begingroup\$ That should only work if
deltaTimeis relatively short. If the delta is longer thansleepDurationthen this will fail. \$\endgroup\$James Curran– James Curran2017-06-07 13:40:08 +00:00Commented Jun 7, 2017 at 13:40 - \$\begingroup\$ if sleepDuration > Time.deltaTime, then, you don't need such system. \$\endgroup\$Hellium– Hellium2017-06-07 13:44:35 +00:00Commented Jun 7, 2017 at 13:44
- \$\begingroup\$ No. Say deltaTime is 2.75 seconds. You wil have 3 or 4 "active" calls then one "sleep" call, until the 50 second mark, when you should have 8 active calls in a row. Also, in that scenario, the first sleep call comes at 11 seconds -- you reset time to 0 -- it should be set to 1. \$\endgroup\$James Curran– James Curran2017-06-07 14:09:06 +00:00Commented Jun 7, 2017 at 14:09
- 2\$\begingroup\$ In Unity, Time.deltaTime represents the elapsed time between the current frame of the game engine and the last frame (thus, I hope for the final user that Time.deltaTime will never be 2.75 seconds, ....). In this context, you may "miss" a call to the function or a "non"-call to the function, which is not an issue here IMHO. \$\endgroup\$Hellium– Hellium2017-06-07 14:20:06 +00:00Commented Jun 7, 2017 at 14:20
You can do this with a coroutine also. Something like
public class Comp : MonoBehaviour { private bool _shouldCall; Start() { StartCoroutine(UpdateShouldCall) } Update() { if(_shouldCall) CallTheFunction(); } IEnumerator UpdateShouldCall() { while(true) { _shouldCall = true; yield return new WaitForSeconds(10); _shouldCall = false; yield return new WaitForSeconds(2); } } } None of these are correct. Don't base your opinion on answers based on the upvotes. There's far too much wasted computation when you run an update function and check a condition and then just ignore it if it's not matching said condition.
What you actually want to do is have a controlling behavior that enables/disables this component.
Otherwise every frame you're wasting CPU-cycles. If the behavior should be active, turn on the component and once the limit is reached, turn off the component. The manager should manage should manager when the component is enabled.
It's such a waste of resources to run a function every update and check a condition that determines if a function is run. Especially when they've got relatively non-trivial conditions that must be calculated to produce a boolean. Even then, it's still pointless. You want a component that runs that function on Update and then disable it when it's done. There's some edge-cases here where you're using a modulus value to determine whether or not you want to run something every two frames, but that could probably be met with some criticism and more efficient approaches.
Also never use while true is a complete no. You're completely giving up your ability to control the flow of the application. Especially if there's no break in there. It makes much more sense to simply assign a boolean value that the while(b) uses so that you still have control over it, even compared to having while(true) with a break inside that is hit when a boolean is met.
Basically, you're wasting CPU-cycles, especially when calculating the condition, every frame, just to check if you want to use it. Use a manager to enable it when it should run. Also avoid coroutines in mass. They do have their usages, but using it to replace something like update, is just asking for wasted CPU-cycles.
Hope this helps. I've had to learn this things the hard way, unfortunately. I think if you give it some thought, it will become apparently. So essentially, create another component that has an update function (though you should consolidate your update calls to a single engine object and call them from there), and then once the time is over, disable that component. Two minutes later, turn it on, from the manager. Perhaps event-based responses. Rinse and repeat. If you've got one thing doing this, you'll probably not notice it unless you benchmark it, but this is definitely not a good way to build at scale.