I am trying to make my application play a quick .wav before closing the application, but every time it only plays the .wav and does not exit the application. Here is my code:
bool condition = false;
if (condition)
{
SoundPlayer sndPlayer = new SoundPlayer(Project_Rage_v3._1.Properties.Resources.syl);
sndPlayer.Play();
}
else
{
Application.Exit();
}
I know if the bool == true it will do the if function and if the bool == false it will do the else function. But if I don't split them up, the program just quits immediately without playing the sound.
How do I have it play the sound then quit, but only quit after the sound is finished playing?
Remove the else block. You're telling the program to play the sound if condition is true, and exit if condition is false. Put the commands in sequential order and call PlaySync instead of Play (MSDN). This will block the rest of the code from executing until the playing is finished.
bool condition = false;
if (condition)
{
SoundPlayer sndPlayer = new SoundPlayer(Project_Rage_v3._1.Properties.Resources.syl);
sndPlayer.PlaySync();
Application.Exit();
}
Related
I currently have such a piece of code in panelmanager where I open certain panels (I have a similar piece of code for closing panels). But I wanted to make sure that after pressing the button, there was no instant switching between panels, but with a delay. I read that it can be done with coroutines, but I haven't succeeded yet, since I'm probably using it incorrectly. Please tell me how I can implement such a delay correctly, I'm a complete noob..
public void OpenPanel(string name)
{
switch (name)
{
case nameof(MainMenu):
StartCoroutine(CoroutineSample());
MainMenuPanel.gameObject.SetActive(true);
break;
case nameof(LevelsPanel):
StartCoroutine(CoroutineSample());
LevelsPanel1.gameObject.SetActive(true);
break;
}
}
private void Start()
{
StartCoroutine(CoroutineSample());
}
private IEnumerator CoroutineSample()
{
yield return new WaitForSeconds(2);
}
You have to call the
MainMenuPanel.gameObject.SetActive(true);
inside the coroutine after the yield return line.
As it is in your code the StartCoroutine just starts the coroutine and then continues immediately with the next line. So it won't wait. And the WaitForSeconds call doesn't help anything because after the 2 seconds the coroutine doesn't do anything.
I am using this code along with other methods after it, but cannot get the form I've been using in designer to pop up. The only thing that pops up successfully is the MessageBox asking the player if they would like to play as X. I've tried commenting that out to see if that's the issue for why it won't load, but I'm completely lost as to why my form will not load at all.
namespace mmelichar_Topic6_Activity13
{
public partial class mmelichar_TicTacToe : Form
{
int player = 0;
int position;
int turn = 0;
int playerMove;
int firstMove;
int secondMove;
string[,] location = new string[3, 3] { { "", "", "" }, { "", "", "" }, { "", "", "" } };
public mmelichar_TicTacToe()
{
InitializeComponent();
}
private void mmelichar_TicTacToe_Load(object sender, EventArgs e)
{
//There's only two moves that have to be hard-coded for AI to be able to
//play tic-tac-toe near perfectly, each game /should/ result in a tie or
//a win for the CPU. Other than that, the tryWin and tryBlock methods
//should be able to win the game if there is an availability for that,
//or block the opponent from winning if they cannot win quite yet.
Random rnd = new Random();
DialogResult dialogResult = MessageBox.Show("Do you want to play as X?", "Player Choice", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
if (dialogResult == DialogResult.Yes)
{
player = 1;
}
else if (dialogResult == DialogResult.No)
{
player = 0;
}
//player is O
if (player == 0 && turn == 0)
{
firstTurnCPU();
//player turn
mre.WaitOne();
}
//player is X
if (player == 1 && turn == 0)
{
//player turn 1
mre.WaitOne();
//cpu turn 1
firstTurnCPU();
//player turn 2
mre.WaitOne();
//cpu turn 2
secondTurnCPU();
//player turn 3
mre.WaitOne();
//cpu turn 3
tryWin();
tryBlock();
//player turn 4
mre.WaitOne();
//cpu turn 4
tryWin();
tryBlock();
//player turn
}
}
private readonly ManualResetEvent mre = new ManualResetEvent(false);
private void playerTurn_EventHandler(object sender, EventArgs e)
{
mre.Set();
}
edit: updated code to remove while loop and include my ManualResetEvent
You have an infinite loop:
while (playing)
{
// perform a bunch of logic
// but probably don't do anything async or properly interact with the UI
}
An infinite loop on the UI threat would certainly prevent the UI from ever drawing to the screen. (It may also be running away with the CPU a lot more than you want it to.)
If you really do want a "game loop" style of game construction, there are approaches you can take in Windows Forms. But now would be a good time to really think through the design of the game before going that route.
Windows Forms is highly event-driven. It's idle most of the time, and responds when users interact with the UI. (A click here, a mouse-over there, etc.) If your game fits that structure (turn based, etc.) then use that structure since it's more native to Windows Forms.
You can also combine the two, using Windows Forms events to process user interactions, but when the program launches you can create a separate thread for your game loop to process ongoing events/logic in the background. Just make sure that loop isn't killing the CPU by constantly running. It's reasonable to sleep the thread for a moment on each iteration of the loop.
I had two problems within my code:
First, an infinite while loop that I forgot to remove, increasing CPU usage needlessly.
Second, use of ManualResetEvent instead of just using normal flow within the program to hand control back and forth from player to CPU and vice-versa.
Winforms C#
I'm implementing a power up system in my snake game and every time i run the method to spawn a power up the window completely stops as if it was processing something heavy but forever. i don't get any error nor windows asking to close window.
this is my script:
private void spawn_powerup()
{
int r = random.Next(0, Powers.Length);
foreach (Power_Up item in Powers)
{
if (item.code == r)
{
bool done = false;
do
{
int X = random.Next(0, grid_size);
int Y = random.Next(0, grid_size);
if (matriz[X, Y] == background_color)
{
matriz[X, Y] = item.Pixel_color;
done = true;
item.placed = true;
}
} while (done);
return;
}
}
}
You never set done to false inside your do-while-loop.
So if within your first iteration your if expression evaluates to true, done is set to true and your do-while runs forever
For what I think you want to achieve change your do-while condition to !done, but then you have to make sure that your if condition is met after some time (but in a snake game this shouldn't be much of an issue).
First of all your while logic is wrong. While loop continues when it is set to true, and exits whenever it gets false or break; command is issued.
Also you are using Synchronous programming pattern. So your application runs on single thread and whenever you call long running function you application start to wait that long running function to finish. Then it resumes continuing.
Please consider to look Asynchronous Programming in C#
Another Reference
Your loop conditions will never allow it to stop, since you wrote while(done), which means: "while done is set to true", which it always is.
You should change you loop to this:
bool done = false;
do
{
(- your code here -)
} while (!done);
I'm creating a Pop up menu Option in Unity. Now my Problem here is that the coroutine i made in void update is being called so many times. What i mean by that is on my Unity Console the Debug.Logs are incrementing . It should not right because its already coroutine. Could some help me understand more coroutine and help me solve my little problem .
Here is my code:
[SerializeField]
GameObject Option;
[SerializeField]
Button btn,btn2;
[SerializeField]
GameObject open, close;
[SerializeField]
GameObject[] opt;
bool startFinding = false;
void Start()
{
Option.SetActive(false);
Button popUp = btn.GetComponent<Button>();
Button popUp2 = btn2.GetComponent<Button>();
popUp.onClick.AddListener(PopUpOption);
popUp2.onClick.AddListener(ClosePopUp);
}
void Update()
{
if (startFinding)
{
StartCoroutine(GameOptions());
}
}
IEnumerator GameOptions()
{
//Get All the tags
opt = GameObject.FindGameObjectsWithTag("MobileOptions");
if (opt[0].GetComponent<Toggle>().isOn == true && opt[1].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Disable first the check box then choose only 1 option between" + "'rendering'"+ "and" + "'livestreaming'");
}
//Livestreaming
if (opt[0].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Livestreaming Activate");
} else
{
Debug.Log("Livestreaming Deactivate");
}
//Rendering
if (opt[1].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Rendering Activate");
} else
{
Debug.Log("Rendering Deactivate");
}
//Fog
if (opt[2].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Fog Activated");
} else
{
Debug.Log("Fog Deactivated");
}
//Camera Effect
if (opt[3].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Camera Effect Activated");
} else {
Debug.Log("Camera Effect Deactivated");
}
yield return null;
}
void PopUpOption()
{
startFinding = true;
//Disable The Mobile Option Button
open.SetActive(false);
//Enable the Close Option Button
close.SetActive(true);
//activate the Mobile Options
Option.SetActive(true);
}
void ClosePopUp()
{
startFinding = false;
//eanble the mobile option button
open.SetActive(true);
//disable the close option button
close.SetActive(false);
//deactivate the Mobile Option
Option.SetActive(false);
}
Here is how coroutines work:
Let's say I have a couroutine function called MyRoutine (in your case, you called it GameOptions)
private IEnumerator MyRoutine()
Then, anywhere in my code, calling
StartCoroutine(MyRoutine));
Is going to simply call MyRoutine like any usual method. So if you call it in update, it will be called all the time, as any method would. This is not what you want. What make coroutines special is that you can use the yield keyword in them. There are many ways to use it but the most used (and simple) one is to do yield return null
yield return null means "Stop this coroutine, but resume the execution on next frame". You don't need to call any other function (certainly not StartCoroutine). The execution will resume next frame.
To go back to what you posted in your question, you wrote yield return null at the end. So your method is executing, and just at the end, stops and resumes next frame, but since there is nothing left to do, it exits on the next frame.
A typical way to use coroutines is to have the yield return null in a while loop, so when it resumes, it continues the loop. Here is an example that do it
private IEnumerator MyRoutine()
{
while(running) //running is a member bool that you could set to false to exit
{
// Do all the stuff you want to do in ONE frame
// ...
yield return null;
}
}
Typically, the StartCoroutine would be called in the Start() function, or later when an event is triggered.
If you want to know more about coroutine, or check that you understood them properly, check out this page: https://docs.unity3d.com/Manual/Coroutines.html
or this video https://unity3d.com/learn/tutorials/topics/scripting/coroutines
// Edit: quickly present one useful option
In the snippet above, the while loop is very similar to the Update function (the inside of the loop is executed each frame). One nice option is to replace
yield return null
by
yield return new WaitForSeconds(waitTime)
where waitTime is a the time you want to wait before resuming, in seconds
// End of edit
Do not use StartCoroutine() in the Update method. Call it in another method and use a while loop inside your coroutine function if needed. Just control your StartCoroutine() outside of Update method
Update is called every frame, if your condition is ever true, you launch your coroutine every frame.
Just set down your flag to only join 1 time.
void Update()
{
if (startFinding)
{
startFinding = false;
StartCoroutine(GameOptions());
}
}
I would like my app to show the message on the first back button press as "Please touch back button again to quit the app" and when it is pressed again the app should quit. I think I have added appropriate code but it doesn't work.
The script is attached as a component to the canvas element.
The script contains the public variable which I assigned the panel(Child of canvas) UI element.
Scene hierarchy
Observed:
When I pressed the back button the text appears but only a fraction of a second and then disappear all of a sudden and the next back button press did not resulted in app quit.
Desired
On first back button press it should show the message and with in say 3 seconds if the second back button pressed the app should quit.
Relevant information:
Unity 2017.1.0f3
Here is the Code link :
https://gist.github.com/bmohanrajbit27/431221fc80e0b247649289fd136f9cfb
public class ChangeSceneScript : MonoBehaviour
{
private bool iQuit = false;
public GameObject quitobject;
void Update()
{
if (iQuit == true)
{
if (Input.GetKeyDown(KeyCode.Escape))
{
Application.Quit();
}
}
if (Input.GetKeyDown(KeyCode.Escape))
{
quitobject.SetActive(true);
iQuit = true;
StartCoroutine(QuitingTimer());
}
}
IEnumerator QuitingTimer()
{
yield return new WaitForSeconds(3);
iQuit = false;
quitobject.SetActive(false);
}
}
I've seen few instances where Application.Quit(); did not work on Android. When this happens, use System.Diagnostics.Process.GetCurrentProcess().Kill(); to exit out of the program.
Now, for your timer issue, start a coroutine in the Update function when input is pressed for the first time. Use a flag to make sure that this coroutine function is not started again until the last one has finished. A boolean variable is fine.
Inside, that coroutine function, don't use yield return new WaitForSeconds(3); to wait for the timer. Use a while loop with the combination of yield return null; to wait until the timer is done. Increment the timer with Time.deltaTime each frame. Now, you can easily check for the second press in that coroutine function and exit if pressed.
If you also want this to work in the Editor, you have to use UnityEditor.EditorApplication.isPlaying = false; to exit. The example below should also work in the Editor. See the comments in the code if you have any question.
public GameObject quitobject;
private bool clickedBefore = false;
void Update()
{
//Check input for the first time
if (Input.GetKeyDown(KeyCode.Escape) && !clickedBefore)
{
Debug.Log("Back Button pressed for the first time");
//Set to false so that this input is not checked again. It will be checked in the coroutine function instead
clickedBefore = true;
//Activate Quit Object
quitobject.SetActive(true);
//Start quit timer
StartCoroutine(quitingTimer());
}
}
IEnumerator quitingTimer()
{
//Wait for a frame so that Input.GetKeyDown is no longer true
yield return null;
//3 seconds timer
const float timerTime = 3f;
float counter = 0;
while (counter < timerTime)
{
//Increment counter while it is < timer time(3)
counter += Time.deltaTime;
//Check if Input is pressed again while timer is running then quit/exit if is
if (Input.GetKeyDown(KeyCode.Escape))
{
Debug.Log("Back Button pressed for the second time. EXITING.....");
Quit();
}
//Wait for a frame so that Unity does not freeze
yield return null;
}
Debug.Log("Timer finished...Back Button was NOT pressed for the second time within: '" + timerTime + "' seconds");
//Timer has finished and NO QUIT(NO second press) so deactivate
quitobject.SetActive(false);
//Reset clickedBefore so that Input can be checked again in the Update function
clickedBefore = false;
}
void Quit()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
//Application.Quit();
System.Diagnostics.Process.GetCurrentProcess().Kill();
#endif
}