Unity C# coroutine not working - c#

I am trying to make a Simon game. Obviously, when one color is displayed, it should wait a second or so. I created a coruotine for when the game is showing you what to do. Inside the coroutine, it is supposed to pause first, which when I run the game, it does. Then it goes through and chooses a color and stuff. Then it should loop back up(I made a loop) and pause again, and do everything again. The problem is, the pause only works the first time. Then it displays the other colors for like half a second and ends. I don't understand why once it loops it seems to do everything except for the pause. Here is my code:
void Update () {
if (show == true) {
StartCoroutine(Show());
}
}
IEnumerator Show() {
do {
yield return new WaitForSeconds(2f);
round ++;
if (round == 1) {
on = Random.Range(1,5);
}
if (round == 2) {
on = Random.Range(1,5);
}
if (round == 3) {
on = Random.Range(1,5);
}
if (round == 4) {
on = Random.Range(1,5);
}
// Turn on/off lights
if (on == 1) {
green.GetComponent<Renderer>().material = greenMat;
}
if (on != 1) {
green.GetComponent<Renderer>().material = greenOff;
}
if (on == 2) {
red.GetComponent<Renderer>().material = redMat;
}
if (on != 2) {
red.GetComponent<Renderer>().material = redOff;
}
if (on == 3) {
yellow.GetComponent<Renderer>().material = yellowMat;
}
if (on != 3) {
yellow.GetComponent<Renderer>().material = yellowOff;
}
if (on == 4) {
blue.GetComponent<Renderer>().material = blueMat;
}
if (on != 4) {
blue.GetComponent<Renderer>().material = blueOff;
}
}while(show == true);
}

I wrote a coroutine which may help you. Put it on start.
IEnumerator Show()
{
while (true) // or show
{
yield return new WaitForSeconds(2f);
round ++;
if (round >= 1 && round <= 4)
on = Random.Range(1,5);
green.GetComponent<Renderer>().material = on == 1 ? greenMat : greenOff;
//Same for the other...
}
}

Related

How to improve UI Selection logic?

So I have this piece of code to select objects in the UI on hover, and to store the currently hovered element in the class scope with hoveredAction.
I feel like there would be a more elegant approach of this logic. I know that I could add one more check to avoid some computation if the raycasted object is the same as the stored one, but the "boilerplate" else if (hoveredAction != null) would persist.
This isn't the first time I come across this kind of logic, but this time it is simple enough for me to summarize it, any Idea would help.
void Update()
{
pointerEventData = new PointerEventData(eventSystem)
{
position = Input.mousePosition
};
List<RaycastResult> results = new();
raycaster.Raycast(pointerEventData, results);
if (results.Count > 0)
{
var result = results[0];
var obj = allowedActions.Where(a => result.gameObject == a.UiObject.gameObject);
if (obj.Count() > 0)
{
hoveredAction = obj.First();
hoveredAction.UiObject.GetComponent<Image>().color = shown;
}
else if(hoveredAction != null)
{
hoveredAction.UiObject.GetComponent<Image>().color = hidden;
hoveredAction = null;
}
}
else if (hoveredAction != null)
{
hoveredAction.UiObject.GetComponent<Image>().color = hidden;
hoveredAction = null;
}
}
Note that yours this is also not complete: In the case if (obj.Count() > 0) you also will want to set any previous hit to hidden ... or skip both if previous hit == current
I would split it up and only track the newCurrent hit an do e.g.
void Update()
{
pointerEventData = new PointerEventData(eventSystem)
{
position = Input.mousePosition
};
List<RaycastResult> results = new();
raycaster.Raycast(pointerEventData, results);
// whenever you deal with linq make sure to iterate only once!
Gamebject currentHoveredAction = results.Count == 0 ? null : allowedActions.Where(a => result.gameObject == a.UiObject.gameObject).FirstOrDefault();
// This is
// - either you hit a different object than before
// - you didn't hit anything before but do now
// - or you did hit something before but nothing now
if(hoveredAction != currentHoveredAction)
{
// if there is a previous hit reset it
if (hoveredAction != null)
{
hoveredAction.UiObject.GetComponent<Image>().color = hidden;
}
// if there is a current hit set it
if(currentHoveredAction != null)
{
currentHoveredAction.UiObject.GetComponent<Image>().color = show;
}
// either way store the new result
hoveredAction = currentHoveredAction;
}
}

A followup if statement

My question is a bit hard to describe so I have written a hypothetical code (nonfunctioning) down below and I wonder if there is a similar alternative in C#:
if (Red == true)
{
i -= 3;
}
else if (Yellow == true)
{
i -= 2
}
then
{
list.Clear();
}
else{}
A "then" function of sorts that both if statements follow if one where to execute. The use of this would simply be so that I do not need to do in this case a list.Clear(); in every if statement.
No there is no syntax construct like your then but you can create a method that clear the list and accept and returns the value to decrement
private int ClearAndDecrementBy(int decrement)
{
list.Clear();
return decrement;
}
and call it as
if(Red)
{
i -= ClearAndDecrementBy(3);
}
else if(Yellow)
{
i -= ClearAndDecrementBy(2);
}
else
{
}
Not really sure that there is any advantage though. The list should be declared at the global class level and this is never a good practice if it is needed only to make it work in this way. So, adding the call to list.Clear inside the if blocks seems more clear and it won't do any harm
Try this:
if(Red == true)
{
i -= 3;
}
else if(Yellow == true)
{
i -= 2
}
else
{
}
if(Red == True || Yellow == true) //You can add more like: Blue == true
{
list.Clear();
}
You could get rid of the "then" statement from your pseudo code and add the following line to the end of everything.
if (Red || Yellow)
{
list.Clear();
}
Try this:
if (Red || Yellow)
{
i = Red ? -3 : -2;
list.Clear();
}
else { }

Visual Studio C# delay function for if statment

I'm looking for a way to create a delay in my code without stopping the rest of the code in this time frame. It has to work as follows. If a condition is true for X amount of seconds, set the alarm bool High. The code below is a part of code where I set the alarms but there needs to be a delay.
I'm very new to C# so try to dumb it down a bit, maybe a small example or links.
if (Valve.Parameter.NormallyOpen == false)
{
if (Valve.Status.Output == true & Valve.Status.Opened == false)
{
Valve.Alarm.NotOpened = true;
}
if (Valve.Status.Output == false & Valve.Status.Closed == false)
{
Valve.Alarm.NotClosed = true;
}
}
Some background info: this will be a valve control block in PLCNext written in C#. The control block should give a alarm if the valve is send open but does not give open feedback within X seconds (takes time to open).
using System;
using System.Threading;
using System.Iec61131Lib;
using Iec61131.Engineering.Prototypes.Types;
using Iec61131.Engineering.Prototypes.Variables;
using Iec61131.Engineering.Prototypes.Methods;
using Iec61131.Engineering.Prototypes.Common;
namespace EclrFirmwareLibrary1
{
[FunctionBlock]
public class Valve_Test
{
[InOut]
public Valve Valve;
[Output]
public bool Output;
double seconds = 1.00; // 1 Second Interval
[Initialization]
public void __Init()
{
//
// TODO: Initialize the variables of the function block here
//
}
[Execution]
public void __Process()
{
Valve.Status.Interlock = Valve.Control.Interlock;
Valve.Status.Opened = Valve.Control.FB_Open;
Valve.Status.Closed = Valve.Control.FB_Closed;
if (Valve.Control.Manual_Mode == true){
Valve.Status.Manual_Mode = true;
Valve.Status.Auto_Mode = false;
}
if (Valve.Control.Auto_Mode == true & Valve.Control.Manual_Mode == false){
Valve.Status.Manual_Mode = false;
Valve.Status.Auto_Mode = true;
}
if(Valve.Status.Interlock == false & Valve.Alarm.General == false)
{
if (Valve.Status.Manual_Mode & Valve.Control.Manual_Control)
{
Output = true;
Valve.Status.Output = true;
}
else if (Valve.Status.Auto_Mode & Valve.Control.Auto_Control)
{
Output = true;
Valve.Status.Output = true;
}
else
{
Output = false;
Valve.Status.Output = false;
}
}
else
{
Output = false;
Valve.Status.Output = false;
}
//Alarms---------------------------------------------------
var startTimeSpan = TimeSpan.Zero;
var periodTimeSpan = TimeSpan.FromSeconds(seconds);
var timer = new System.Threading.Timer((e) =>
{
CheckValve();
}, null, startTimeSpan, periodTimeSpan);
}
private void CheckValve()
{
if (Valve.Parameter.NormallyOpen == false)
{
if (Valve.Status.Output == true & Valve.Status.Opened == false)
{
Valve.Alarm.NotOpened = true;
}
if (Valve.Status.Output == false & Valve.Status.Closed == false)
{
Valve.Alarm.NotClosed = true;
}
}
else
{
if (Valve.Status.Output == false & Valve.Status.Opened == false)
{
Valve.Alarm.NotOpened = true;
}
if (Valve.Status.Output == true & Valve.Status.Closed == false)
{
Valve.Alarm.NotClosed = true;
}
}
if (Valve.Status.Opened == true & Valve.Status.Closed == true)
{
Valve.Alarm.OpenedAndClosed = true;
}
if (Valve.Alarm.NotClosed || Valve.Alarm.NotOpened || Valve.Alarm.OpenedAndClosed)
{
Valve.Alarm.General = true;
}
}
}
}
The important thing to note in this case is that the device you are using includes a .NET runtime implementation that is a subset and specialisation of the complete .NET runtime. This "eCLR" (Embedded CLR) is designed to operate in the deterministic real-time context required for many industrial control applications.
The details of this implementation can be seen in the PLCnext Info Center
When you create a PLCnext C# project in Visual Studio, the project includes a Programming Reference (.chm file). You will see from this reference that the Timer class is not included in the eCLR.
To your question:
There are a number of ways you can implement a delay function in a PLCnext C# function. Perhaps the simplest is to use the DateTime.Now property to get the current system time when the alarm condition is first seen, and remember this value. Then, on each call to the Process method, you can compare the current time to the time when the alarm condition was first seen. When the time period exceeds the preset time, then the output is set.
For more help with questions related to PLCnext Control devices, you can visit the PLCnext Community. There is an active forum there, where you can discuss these sorts of issues with other PLCnext users.
To call a method each x seconds you can use the TimeSpan Method provided by C# and just pass a method called CheckValve, where you put all your code into.
TimeSpan Example:
double seconds = 1.00; // 1 Second Interval
var startTimeSpan = TimeSpan.Zero;
var periodTimeSpan = TimeSpan.FromSeconds(seconds);
var timer = new System.Threading.Timer((e) =>
{
CheckValve();
}, null, startTimeSpan, periodTimeSpan);
I would also advise you to refactor some of your code:
You can replace bool == true checks with just the bool and bool == false checks with just the bool and a ! before the name.
I would also add an else if so if the if is true you don't need to check the other statement.
CheckValve Method:
private void CheckValve() {
if (!Valve.Parameter.NormallyOpen) {
if (Valve.Status.Output && !Valve.Status.Opened) {
Valve.Alarm.NotOpened = true;
}
else if (!Valve.Status.Output && !Valve.Status.Closed)
{
Valve.Alarm.NotClosed = true;
}
}
}

How to "set" a selected camera with combobox in Windows 10 Universal App

I am developing windows application.I want to switch from Front Camera to Back Camera with a Combobox in Windows Universal App or in WPF.
I have coded something but I don't get where I made a mistake.
Here is my code:
<ComboBox x:Name="SettingsCamera" HorizontalAlignment="Stretch" Grid.Row="1" Grid.Column="0" Margin="0,5" SelectionChanged="SettingsCamera_SelectionChanged"/>
private async void InitializeCameraAsync()
{DeviceInformation device = FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel desiredPanel);
var devices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(Windows.Devices.Enumeration.DeviceClass.VideoCapture);
SettingsCamera.Items.Clear();
//_deviceList = new List<Windows.Devices.Enumeration.DeviceInformation>();
// Add the devices to deviceList
if (devices.Count > 0)
{
for (var i = 0; i < devices.Count; i++)
{
// _deviceList.Add(devices[i]);
SettingsCamera.Items.Add(devices[i].Name);
}
}
else
{
Debug.WriteLine("No camera device is found ");
}
}
private async void SettingsCamera_SelectionChanged(object sender,SelectionChangedEventArgs e){
if (SettingsCamera.SelectedIndex == 0)
{
try
{
var allVideoDevices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
//SettingsMicrophone.Items.Clear();
var frontCamera = allVideoDevices.FirstOrDefault(d => d.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front);
if (allVideoDevices.Count == 0)
{
SettingsCamera.Items.Add(frontCamera.Name);
}
}
catch (NullReferenceException)
{
//audioExist = false;
SettingsCamera.Items.Add("No michrophone on your system");
}
}
else if (SettingsCamera.SelectedIndex == 1 && SettingsCamera.SelectedIndex == 2 && SettingsCamera.SelectedIndex == 3)
{
try
{
var allVideoDevices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
var backCamera = allVideoDevices.FirstOrDefault(d => d.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front);
//SettingsMicrophone.Items.Clear();
if (allVideoDevices.Count >=2)
{
SettingsCamera.Items.Add(backCamera.Name);
}
SettingsCamera.Items.Add(backCamera.Name);
//make first cam default
}
catch (NullReferenceException)
{
//audioExist = false;
SettingsCamera.Items.Add("No michrophone on your system");
}
}
}
I tested your code with MobileEmulator version 10240, the following image shows what I get:
By my side, if you select the item "SocCaptureSim RFC", the SelectedIndex is 0, and if you select the item "SocCaptureSim FFC", the SelectedIndex is 1.
But according to your code of SettingsCamera_SelectionChanged(object sender,SelectionChangedEventArgs e) method, when I select the item "SocCaptureSim FFC", nothing will happen, because your condition of the else if is SettingsCamera.SelectedIndex == 1 && SettingsCamera.SelectedIndex == 2 && SettingsCamera.SelectedIndex == 3, a ComboBox doesn't support mutil-selection.
So maybe what you want is else if (SettingsCamera.SelectedIndex == 1 || SettingsCamera.SelectedIndex == 2 || SettingsCamera.SelectedIndex == 3).
There are two other problems in your code, DeviceInformation device = FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel desiredPanel); in the InitializeCameraAsync() method, although you didn't post the code of FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel desiredPanel) method, according to the left code, I think you pass the wrong parameter here.
And in the InitializeCameraAsync() method, I can understand that you want to find all the camera device and add the device to the list of ComboBox, but when you select index 0,
if (allVideoDevices.Count == 0)
{
SettingsCamera.Items.Add(frontCamera.Name);
}
This code make no sense here, if allVideoDevices.Count == 0, this var frontCamera = allVideoDevices.FirstOrDefault(d => d.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front); will cause an null pointer error at first.
Since I didn't see any of your code for opening a camera to take photos or record, you can refer to the official Basic camera app sample, see how to get a camera to work.

Unity3D Code repeats too much

I am trying to make if you got enough gold then comes you need rock, if you got enough rock then you need enough gold, but if you have both then you can "Upgrade". But if you got both then it goes back to the you need gold.
void Update()
{
if(enoughgold == true & enoughrocks == true)
{
Upgrade.text = "Upgrade to 2014!";
}
if(sellrocks.gold > 9999)
{
enoughgold = true;
}
else
{
enoughgold = false;
}
if(click.rock > 2999)
{
enoughrocks = true;
}
else
{
enoughrocks = false;
}
if(enoughgold == true)
{
Upgrade.text = "You need 3,000 Rocks!";
}
else
{
Upgrade.text = "You need 10,000 Gold!";
}
if (enoughrocks == true)
{
Upgrade.text = "You need 10,000 Gold!";
}
else
{
Upgrade.text = "You need 3,000 Rocks!";
}
}
How about something like this? You first see whether the user has enough gold and rocks and then do the checking.
I have simplified if (enoughgold == true) to if (enoughgold) as the == true is redundant.
void Update()
{
enoughgold = sellrocks.gold > 9999;
enoughrocks = click.rock > 2999;
if (enoughgold && enoughrocks)
Upgrade.text = "Upgrade to 2014!";
else if (enoughgold && !enoughrocks)
Upgrade.text = "You need 3,000 Rocks!";
else if (!enoughgold && enoughrocks)
Upgrade.text = "You need 10,000 Gold!";
else if (!enoughgold && !enoughrocks)
Upgrade.text = "You need 10,000 Gold and 3,000 Rocks!";
}
You could also create an enum to handle all 4 possibilities: if the user has only enough rocks, if the user has only enough gold, if the user has enough of both and if the user doesn't have enough of any.

Categories