Condition met, but conditional action not executed in coroutine - c#

I have a coroutine like so:
private IEnumerator ShortFlashes()
{
this.startedShortFlashes = true;
this.finishedShortFlashes = false;
const int maxFlashes = 3;
int numFlashesSoFar = 1;
if (numFlashesSoFar > maxFlashes)
{
Debug.Log("All 3 short flashes finished!");
this.finishedShortFlashes = true;
yield break;
}
while (numFlashesSoFar <= maxFlashes)
{
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = true;
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = false;
Debug.Log("Number of short flashes so far: " + numFlashesSoFar);
numFlashesSoFar++;
}
}
When this coroutine is running, I can see messages in the Unity console enumerating the number of short flashes, just as intended. (Debug.Log("Number of short flashes so far: " + numFlashesSoFar);).
However, Debug.Log("All 3 short flashes finished!"); is never executed, even when numFlashesSoFar exceeds maxFlashes.
This is very inconvenient, because in my Update() method, there are some additional actions that I would like to perform if this.finishedShortFlashes is true.
How can I fix this issue?

You've hardcoded the values.
const int maxFlashes = 3;
int numFlashesSoFar = 1;
if (numFlashesSoFar > maxFlashes)
{
//...
}
if (numFlashesSoFar > maxFlashes) is effectively equal to if (1 > 3), which is never true.
I genuinely don't understand why you structured the code the way you did, which makes it hard for me to understand the core issue here.
This makes much more sense:
const int maxFlashes = 3;
int numFlashesSoFar = 1;
while (numFlashesSoFar <= maxFlashes)
{
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = true;
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = false;
Debug.Log("Number of short flashes so far: " + numFlashesSoFar);
numFlashesSoFar++;
}
Debug.Log("All 3 short flashes finished!");
this.finishedShortFlashes = true;
Note that you don't need the if. When the while loop finishes, you already know that the condition is met (otherwise the while loop would not have finished yet.
I don't understand the purpose of the yield break; in your code. It seems unnecessary, so I removed it.

I think you're missing the point of yield keyword.
Once the execution encounter yield return the execution is transferred back to the calling method and the execution state for the routine is retained. The next time you call that same routine, the execution resumes from where it has been yielded.
More information is available in the official documentation
In that particular case, the line Debug.Log("All 3 short flashes finished!"); is never hit, because when the control steps into the method initially, the numFlashesSoFar variable is set to 1 so the condition is never met. Then it goes into the loop, where the yield keyword is encountered. So the next time, the execution continues from within the loop.

I don't understand why you put that if-statement above the loop. The while loop will continue until the while-statement is false. There is no need for the if-statement, you can simply put the code you have inside your if-statement below the loop:
private IEnumerator ShortFlashes()
{
this.startedShortFlashes = true;
this.finishedShortFlashes = false;
const int maxFlashes = 3;
int numFlashesSoFar = 1;
while (numFlashesSoFar <= maxFlashes)
{
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = true;
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = false;
Debug.Log("Number of short flashes so far: " + numFlashesSoFar);
numFlashesSoFar++;
}
Debug.Log("All 3 short flashes finished!");
this.finishedShortFlashes = true;
}
Also, you can see yield return in a Coroutine as a "temporary break in code", because it waits for the next frame to continue the code. Unless you return a WaitForSeconds of course, then it will wait until the given amount of time has passed.

Related

How to activate and deactivate GameObjects with a delay in Untiy

I am working on a game, that has a keypad puzzle. A specific key combination lights up one by one, which the player must repeat to solve that puzzle. I am going to let the player know what the combination is by activating and deactivating some GameObjects systematically, one by one. As it suggests, there is some time delay between the deactivation of one GameObject and the activation of another. The problem is, in my code, all the GameObjects activate simultaneously instead of one by one, after a delay.
Here is the code:
public string Generate(int length, float delay)
{
// Variables for logic
string combination = "";
int prev = -1; int current = 0;
int rnd = 0;
for (int i = 0; i < length; i++)
{
rnd = Random.Range(0, BUTTONS);
while (rnd == prev)
{
rnd = Random.Range(0, BUTTONS);
}
prev = current;
current = rnd;
combination += current.ToString();
// Activation and Deactivation
StartCoroutine(GenerateDelay(delay, current));
}
return combination;
}
IEnumerator GenerateDelay(float delay, int index)
{
ButtonClicks[index].SetActive(true);
yield return new WaitForSeconds(delay);
ButtonClicks[index].SetActive(false);
}
The loop counter specifies the length of the combination. I believe there is something wrong with the Coroutine I made? Since all the objects activate simultaneously.
Here is the result in the game as well:
We can see, only one button should turn green at a time, but all of them do in this case. Any solutions?
You start all your Coroutines parallel so things happen at the same time.
StartCoroutine does not delay the method which calls it (unless it is yielded as well.
You would need to run the entire loop within a Coroutine in order to delay it as well.
You could simply split up the creation of the combination and while you already return it you start the visualization in the background in parallel
public string Generate(int length, float delay, Action<string> onCombination)
{
// Variables for logic
var combination = List<int>();
var prev = -1;
for (var i = 0; i < length; i++)
{
int rnd;
do
{
rnd = Random.Range(0, BUTTONS);
}
while (rnd == prev);
prev = rnd;
combination.Add(rnd);
}
StartCorouine (ShowCombination(combination, delay));
return string.Join("", combination);
}
private IEnumerator ShowCombination (IEnumerable<int> combination, float delay)
{
foreach(var i in combination)
{
ButtonClicks[i].SetActive(true);
yield return new WaitForSeconds(delay);
ButtonClicks[i].SetActive(false);
}
}
something alot easier then a coroutine is invoking a function, basically create a function to activate/deactivate the wanted object and whenever you want to call it do:
Invoke("FUNCTIONNAME", TIME);
and it will run the function specified after the TIME.

from a bool function return false inside a loop in C#

I was recently told that it is a bad practise to return false from inside a loop, though it might not actually be called more than once. Is it better to have a secondary flag to actually break out of the functions?
foo()
{
if(bar)
//somecode
else
//some other code
}
private static bool bar(int x)
{
for(int i=0;i<10;i++)
{
if(i<x)
{
return false;
break;
}
//some operation
}
Is this break statement necessary, considering the boolean function has already returned out of it by then
The Break statement is unnecessary
Here's why
IL code for foo()
bool flag = this.bar();
if (flag)
{
// some commented code
}
and in IL code for bar() below
Notice the there is no
break;
after the line
return result;
private bool bar()
{
bool result;
int num;
for (int i = 0; i < 10; i = num + 1)
{
bool flag = i < 1;
if (flag)
{
result = false;
return result;
}
num = i;
}
result = true;
return result;
}
break;
is actually removed by the C# compiler when compiling to IL Code
Usually
return is indicator to exit the method and return the result.
break is used when you want to exit the loop, iterative processes.
i think it's not best approach, but you can do that, at least the compiler will know what to do however suggesting you the warning.
you should see the break warning

WinForm C# "new Process" in infinite loop causing application to crash

I have a requirement for which I need my block of code to run infinitely (The exit is an interrupt based on button click).
In each iteration, I am creating a Process, starting it, manipulating the output and then disposing the Process.
void status()
{
do{
Process test1 = new Process();
test1.StartInfo.FileName = "doSomething"; // doSomething is executable
test1.StartInfo.UseShellExecute = false;
test1.StartInfo.CreateNoWindow = true;
test1.StartInfo.RedirectStandardOutput = true;
test1.StartInfo.RedirectStandardError = true;
test1.Start();
string output = test1.StandardOutput.ReadToEnd();
test1.WaitForExit();
if (Regex.IsMatch(output, "DEVICE_READY", RegexOptions.IgnoreCase))
{
pictureBox2.BackColor = Color.Green;
}
else
{
pictureBox2.BackColor = Color.Yellow;
}
test1.Dispose();
}while(true);
}
The problem is the application is crashing with this code. If I just remove the loop, it works fine.
I checked while debugging, memory usage of the application keeps on increasing with each iteration of the loop, making the application crash at one point.
What I understood is Dispose() will release all the resources... so memory should not increase with each iteration.
Could someone please help to understand what is causing the memory usage increase?
What I do when I have to deal with hungry processes (eg. pictures manipulation) is to call the garbage collector explicitly.
This is not the cleanest way (costs a lot), but reasonnably used, it does the trick.
void status()
{
// will be used to explicitly call the garbage collector
const int COLLECT_EVERY_X_ITERATION = 10;
// store the current loop number
int currentIterationCount = 0;
do
{
// increase current iteration count
currentIterationCount++;
using (Process test1 = new Process())
{
test1.StartInfo.FileName = "doSomething"; // doSomething is executable
test1.StartInfo.UseShellExecute = false;
test1.StartInfo.CreateNoWindow = true;
test1.StartInfo.RedirectStandardOutput = true;
test1.StartInfo.RedirectStandardError = true;
test1.Start();
string output = test1.StandardOutput.ReadToEnd();
test1.WaitForExit();
if (Regex.IsMatch(output, "DEVICE_READY", RegexOptions.IgnoreCase))
{
pictureBox2.BackColor = Color.Green;
}
else
{
pictureBox2.BackColor = Color.Yellow;
}
}
// Explicitly call garbage collection every 10 iterations
if (currentIterationCount % COLLECT_EVERY_X_ITERATION == 0)
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
} while (true);
}

Delay in a WinForms app without using "await Task.Delay(x)"

I'm creating a poker app and i want to make some sort of simulation of the throwing of the cards effect and to do that I currently use await Task.Delay(x); However this requires async Task and if i switch this method where i do the task.delay(x) to async i will have to change at least 5-6 more into async as well. I suppose this is fine for someone that understand the exact way the asynchronus works. Currently im getting a lot of logic errors simply because i obviously don't know how async and await operators work.. In other words I'm newbie, and is there any alternative to this exact line await Task.Delay(x); I wont use anything else that is connected to async just this line.
Here's the code :
private async Task Shuffle()
{
Bitmap refreshBackImage = new Bitmap(getBack);
bCall.Enabled = false;
bRaise.Enabled = false;
bFold.Enabled = false;
bCheck.Enabled = false;
MaximizeBox = false;
MinimizeBox = false;
bool check = false;
horizontal = tbChips.Left - _settings.Width * 2 - 15;
vertical = pbTimer.Top - _settings.Height - (_settings.Height) / 7;
RNGCrypto random = new RNGCrypto();
for (i = ImgLocation.Length; i > 0; i--)
{
int j = random.Next(i);
string k = ImgLocation[j];
ImgLocation[j] = ImgLocation[i - 1];
ImgLocation[i - 1] = k;
}
for (i = 0; i < 17; i++)
{
Deck[i] = Image.FromFile(ImgLocation[i]);
string[] charsToRemove = { getCards, ".png", "\\" };
foreach (string c in charsToRemove)
{
ImgLocation[i] = ImgLocation[i].Replace(c, string.Empty);
}
Reserve[i] = int.Parse(ImgLocation[i]) - 1;
Holder[i] = new PictureBox
{
SizeMode = PictureBoxSizeMode.StretchImage,
Height = _settings.Height,
Width = _settings.Width
};
Controls.Add(Holder[i]);
Holder[i].Name = "pb" + i;
await Task.Delay(150);
#region Throwing Cards
SetPlayers(Player, i, ref check, 560, 470, refreshBackImage);
SetPlayers(Bot1, i, ref check, 15, 420, refreshBackImage);
SetPlayers(Bot2, i, ref check, 75, 65, refreshBackImage);
SetPlayers(Bot3, i, ref check, 590, 25, refreshBackImage);
SetPlayers(Bot4, i, ref check, 1115, 65, refreshBackImage);
SetPlayers(Bot5, i, ref check, 1160, 420, refreshBackImage);
if (i >= 12)
{
Holder[12].Tag = Reserve[12];
if (i > 12) Holder[13].Tag = Reserve[13];
if (i > 13) Holder[14].Tag = Reserve[14];
if (i > 14) Holder[15].Tag = Reserve[15];
if (i > 15)
{
Holder[16].Tag = Reserve[16];
}
if (!check)
{
horizontal = 410;
vertical = 265;
}
check = true;
if (Holder[i] != null)
{
Holder[i].Anchor = AnchorStyles.None;
Holder[i].Image = refreshBackImage;
//Holder[i].Image = Deck[i];
Holder[i].Location = new Point(horizontal, vertical);
horizontal += 110;
}
}
#endregion
Bot1 = (Bot)FoldedPlayer(Bot1);
Bot2 = (Bot)FoldedPlayer(Bot2);
Bot3 = (Bot)FoldedPlayer(Bot3);
Bot4 = (Bot)FoldedPlayer(Bot4);
Bot5 = (Bot)FoldedPlayer(Bot5);
if (i == 16)
{
if (!restart)
{
MaximizeBox = true;
MinimizeBox = true;
}
Turns();
}
}
Ending();
}
For things like animations, you might want the System.Windows.Forms.Timer component, which raises a periodic event.
You can configure the interval as well as temporarily disabling it entirely.
Note that after the delay, you'll be back to the top of the handler function... it doesn't automatically keep track of where you are in your sequence the way the await keyword automatically resumes at the next line. So you'll need some counter or such to let you know what step of the animation is coming next.
I'm not sure if you want to do the async, because it wont come back until it's complete. Meaning if you want something timed. Async will have some trouble providing this for you. Because you'll either have to wait for the response which may be an issue. Especially if its a game. Watch how the async functions in debug and you'll understand. An event trigger, or just a timed event will work much better because you'll have the control of when it triggers or how a long you wait for the event. This will get rid of your delay all together or it should.
In your case, a simple trigger event at the time, will display the throwing of cards, and once finished will continue on with your function. That's simple terms, you may have to do a few things like waiting until that trigger is finished. Either way there is a ton of information regarding events. Have a look before you go for async. Since it's forms you can do it however you please, if it for windows phone or other types of systems some require async only.
this should help:
https://msdn.microsoft.com/en-us/library/wkzf914z(v=vs.90).aspx

How to "interrupt" a while loop in C# and return to the same place (without using any Threading)

I need help switching between while loops and resuming to the exact state that they were in.
An example would be this:
while(1==1)
{
x++;
x++;
x++;
x++;
}
while(1==1)
{
Console.WriteLine("X=" + x);
Console.WriteLine("X=" + x);
Console.WriteLine("X=" + x);
Console.WriteLine("X=" + x);
}
I am working for a project that lets you create an OS in C#. It is called Cosmos and a quick google search should land you with some info.
What I need to do is to pause one of the loops, and resume (or start) a different loop until the time is over, then that loop will be paused and a different one will be resumed and so on in an infinite cycle.
I am trying to make a simple task scheduler, and I plan to make a lot more changes than simple while loop switching, but I would like this as a primitive state and for testing.
So, what would need to happen is that one while loop executes, then is paused and the second one is executed. What would need to happen is that each loop would pause and switch to a different one, effectively seeming as if they are running at the same time. So, what would happen is that x would increase, then be printed, x increased, and so on.
Well, we could do coöperative (nonpreëmptive) multi-tasking by creating state machines to handle each loop:
private interface IStateMachine
{
void DoNext();
}
private class Loop0 : IStateMachine
{
private int _state;
private int x;
public void DoNext()
{
switch (_state)
{
case 0:
x++;
_state = 1;
break;
case 1:
x++; // This is of course the same as previous, but I'm matching
// the code in your question. There's no reason why it need
// not be something else.
_state = 2;
break;
case 2:
x++;
_state = 3;
break;
case 3:
x++;
_state = 0;
break;
}
}
}
private class Loop1 : IStateMachine
{
private int _state;
private int x;
public void DoNext()
{
switch (_state)
{
case 0:
Console.WriteLine("X=" + x);
_state = 1;
break;
case 1:
Console.WriteLine("X=" + x);
_state = 2;
break;
case 2:
Console.WriteLine("X=" + x);
_state = 3;
break;
case 3:
Console.WriteLine("X=" + x);
_state = 0;
break;
}
}
}
private static void Driver()
{
// We could have all manner of mechanisms for deciding which to call, e.g. keep calling one and
// then the other, and so on. I'm going to do a simple time-based one here:
var stateMachines = new IStateMachine[] { new Loop0(), new Loop1() };
for (int i = 0;; i = (i + 1) % stateMachines.Length)
{
var cur = stateMachines [i];
DateTime until = DateTime.UtcNow.AddMilliseconds (100);
do
{
cur.DoNext ();
} while (DateTime.UtcNow < until);
}
}
There are two big problems with this:
The x in each is a separate x. We need to box the int or wrap it in a reference type so that both methods can be accessing the same variable.
The relationship between your loops and these state machines isn't very clear.
Luckily there already exists a way (indeed more than one) to write a method in C# that is turned into a state-machine with a method for moving to the next state that handles both of these issues:
private static int x;
private static IEnumerator Loop0()
{
for(;;)
{
x++;
yield return null;
x++;
yield return null;
x++;
yield return null;
x++;
yield return null;
}
}
private static IEnumerator Loop1()
{
for(;;)
{
Console.WriteLine("X=" + x);
yield return null;
Console.WriteLine("X=" + x);
yield return null;
Console.WriteLine("X=" + x);
yield return null;
Console.WriteLine("X=" + x);
yield return null;
}
}
private static void Driver()
{
// Again, I'm going to do a simple time-based mechanism here:
var stateMachines = new IEnumerator[] { Loop0(), Loop1() };
for (int i = 0;; i = (i + 1) % stateMachines.Length)
{
var cur = stateMachines [i];
DateTime until = DateTime.UtcNow.AddMilliseconds (100);
do
{
cur.MoveNext ();
} while (DateTime.UtcNow < until);
}
}
Now not only is it easy to see how this relates to your loops (each of the two methods have the same loop, just with added yield return statements), but the sharing of x is handled for us too, so this example actually shows it increasing, rather than an unseen x incrementing and a different x that is always 0 being displayed.
We can also use the value yielded to provide information about what our coöperative "thread" wants to do. For example, returning true to always give up its time slice (equivalent to calling Thread.Yield() in C# multi-threaded code):
private static int x;
private static IEnumerator<bool> Loop0()
{
for(;;)
{
x++;
yield return false;
x++;
yield return false;
x++;
yield return false;
x++;
yield return true;
}
}
private static IEnumerator<bool> Loop1()
{
for(;;)
{
Console.WriteLine("X=" + x);
yield return false;
Console.WriteLine("X=" + x);
yield return false;
Console.WriteLine("X=" + x);
yield return false;
Console.WriteLine("X=" + x);
yield return true;
}
}
private static void Driver()
{
// The same simple time-based one mechanism, but this time each coroutine can
// request that the rest of its time-slot be abandoned.
var stateMachines = new IEnumerator<bool>[] { Loop0(), Loop1() };
for (int i = 0;; i = (i + 1) % stateMachines.Length)
{
var cur = stateMachines [i];
DateTime until = DateTime.UtcNow.AddMilliseconds (100);
do
{
cur.MoveNext ();
} while (!cur.Current && DateTime.UtcNow < until);
}
}
As I'm using a bool here I have only two states that affect how Driver() (my simple scheduler) acts. Obviously a richer datatype would allow for more options, but be more complex.
One possibility would be to have your compiler have a type of method that must return void (comparable to how yield and await have restrictions on the return types of methods that use them in C#) which could contain keywords like thread opportunity, thread yield and thread leave which would then be mapped to yield return false, yield return true and yield break in the C# above.
Of course, being coöperative it requires explicit code to say when other "threads" might have an opportunity to run, which in this case is done by the yield return. For the sort of preëmptive multi-threading that we enjoy just writing in C# for the operating systems it can run on, where time slices can end at any point rather than just where we explicitly allow it will require you to compile the source to produce such state machines, without their being instructions in that source. This is still coöperative, but forces that coöperation out of the code when compiling.
Truly preëmptive multi-threading would require that you have some way of storing the current state of each loop when switching to another thread (just as the stack of each thread in a .NET program does). In a virtual OS you could do this by building threads on top of the underlying OS's threads. In a non-virtual OS you're likely going to have to build your threading mechanism closer to the metal, with the scheduler changing the instruction pointer when threads change,

Categories

Resources