Delay before gameobject set active couritine - c#

I try to have a delay for 3 seconds before the game object(two panels) is activated when a button is pressed. I assigned the button on click the panelview() but I could not find how to write the couritine inside as the IEnumerator.
Can somebody help me? Thanks
Here is the part of the code I want to combine the couritine into:
public void panelview()
{
if (totalenergy == 0)
{
panel2.gameObject.SetActive(true);
}
if (totalenergy >= 1)
{
panel.gameObject.SetActive(true);
}
}

In this case you could also use Invoke without any Coroutine:
public void PanelView()
{
Invoke(nameof(ShowPanel), DELAY);
}
private void ShowPanel()
{
if (totalenergy == 0)
{
panel2.gameObject.SetActive(true);
}
else if (totalenergy >= 1)
{
panel.gameObject.SetActive(true);
}
}
Or as a Coroutine
public void PanelView()
{
StartCoroutine (ShowPanelAfterDelay(DELAY));
}
private IEnumerator ShowPanelAfterDelay(float delay)
{
yield return new WaitForSeconds (delay);
if (totalenergy == 0)
{
panel2.gameObject.SetActive(true);
}
else if (totalenergy >= 1)
{
panel.gameObject.SetActive(true);
}
}
where DELAY is the desired delay in seconds.
From a UX point of view though I'd say three seconds is pretty long for giving the user feedback after a button click ;)

This should do the trick (hopefully it compiles - I didn't check it out).
public void OnButtonClick() {
StartCoroutine(Delay());
}
public IEnumerator Delay()
{
yield return new WaitForSeconds(3);
// PanelView();
// or
// code that you want to execute it after 3 seconds
}

Related

Unity 3d Invokerepeating for blinking Images

I am working on a car game project. I have made a UI Panel in which there are two UI Images (Left_Image and Right_Image). When I press "left" button then Left_Image starts blinking and when i press "right" button the Right_Image starts blinking. but what i want is that if Right_Image is already blinking and I press "left" button then Left_Image starts blink but Right_Images should be stopped. I have tried every trick but no luck. Please help.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Indicators_UI : MonoBehaviour {
public GameObject flash_right;
public GameObject flash_left;
public float interval;
void Start()
{
InvokeRepeating("RightFlash", 0, interval);
InvokeRepeating("LeftFlash", 0, interval);
}
void Update ()
{
if (ControlFreak2.CF2Input.GetAxis ("right") != 0) {
if (!IsInvoking("RightFlash"))
InvokeRepeating("RightFlash", 0.35f, 0.35f);
}
else
{
CancelInvoke("RightFlash");
}
if (ControlFreak2.CF2Input.GetAxis ("left") != 0) {
if (!IsInvoking("LeftFlash"))
InvokeRepeating("LeftFlash", 0.35f, 0.35f);
}
else
{
CancelInvoke("LeftFlash");
}
}
void RightFlash()
{
if(flash_right.activeSelf)
flash_right.SetActive(false);
else
flash_right.SetActive(true);
}
void LeftFlash()
{
if(flash_left.activeSelf)
flash_left.SetActive(false);
else
flash_left.SetActive(true);
}
}
In general don't use InvokeRepeating etc with string! It is not very maintainable to call methods via their names. If you want to use them at least rather use nameof to be sure the name is not misspelled.
Then can't you just do
void Update ()
{
if (ControlFreak2.CF2Input.GetAxis ("right") != 0)
{
if (!IsInvoking(nameof(RightFlash)))
{
if(IsInvoking(nameof(LeftFlash)) CancelInvoke(nameof(LeftFlash));
InvokeRepeating(nameof(RightFlash), 0.35f, 0.35f);
}
}
else
{
CancelInvoke(nameof(RightFlash));
}
if (ControlFreak2.CF2Input.GetAxis ("left") != 0)
{
if (!IsInvoking(nameof(LeftFlash)))
{
if(IsInvoking(nameof(RightFlash)) CancelInvoke(nameof(RightFlash));
InvokeRepeating(nameof(LeftFlash), 0.35f, 0.35f);
}
}
else
{
CancelInvoke(nameof(LeftFlash));
}
}
// Btw: These you can implement way shorter this way ;)
void RightFlash()
{
flash_right.SetActive(!flash_right.activeSelf);
}
void LeftFlash()
{
flash_left.SetActive(!flash_left.activeSelf);
}
In your case I would actually rather use simple timers since you need the Update method anyway for getting the user input so why overcomplicating:
private float timerLeft;
private float timerRight;
void Update ()
{
if (ControlFreak2.CF2Input.GetAxis ("right") != 0)
{
timerRight -= Time.deltaTime;
if (timerRight <= 0)
{
timerRight = interval;
RightFlash();
}
}
// simply make them exclusive so only one button is handled at a time
// if you press both at the same time right overrules
else if (ControlFreak2.CF2Input.GetAxis ("left") != 0)
{
timerLeft -= Time.deltaTime;
if (timerLeft <= 0)
{
timerLeft = interval;
LeftFlash();
}
}
}
void RightFlash()
{
flash_right.SetActive(!flash_right.activeSelf);
}
void LeftFlash()
{
flash_left.SetActive(!flash_left.activeSelf);
}

How can I make a C# script execute in a certain order?

I have a script conflict that checks if there is network connection and the version of the game (if it's up to date); but it's skipping those conditions and loading the game directly.
How do I make the script follow this pattern?:
Check if there is internet connection
Check the version of the game
Only load the game if there is an internet connection and the version of the game is current
You'll notice there are some loading function who repeat themselves; I'm not sure which ones to apply.
CheckNetwork.cs
public Text AlertText;
public GameObject alert;
public string URL = "";
public string CurrentVersion;
string latestVersion;
public GameObject newVersionAvailable;
private void Start()
{
StartCoroutine(LoadTxtData(URL));
}
void Awake()
{
if (Application.internetReachability == NetworkReachability.NotReachable)
{
alert.SetActive(true);
AlertText.text = "Sorry, you have to enable your conection to play this game";
if (Input.GetKeyDown(KeyCode.Escape))
Application.Quit();
}
else
{
alert.SetActive(false);
CheckVersion();
}
}
public void CheckVersion()
{
if(CurrentVersion != latestVersion)
{
newVersionAvailable.SetActive(true);
}
else
{
newVersionAvailable.SetActive(false);
}
}
private IEnumerator LoadTxtData(string url)
{
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.SendWebRequest();
latestVersion = www.ToString();
CheckVersion();
}
public void OpenURL(string url)
{
Application.OpenURL(url);
}
Loading.cs
private Image progressBar;
private void Start()
{
Invoke("LoadAsyncOperation", 3f);
StartCoroutine(LoadAsyncOperation());
}
public IEnumerator LoadAsyncOperation()
{
AsyncOperation gameLevel = SceneManager.LoadSceneAsync(1);
while (gameLevel.progress < 1)
{
progressBar.fillAmount = gameLevel.progress;
yield return new WaitForEndOfFrame();
}
}
Loading2.cs
public GameObject loadingScreenObj;
public Slider slider;
AsyncOperation async;
public void StartGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
public void LoadScreen(int LVL)
{
//StartCoroutine(loadingScreen());
}
IEnumerator LoadingScreen(int lvl)
{
loadingScreenObj.SetActive(true);
async = SceneManager.LoadSceneAsync(lvl);
async.allowSceneActivation = false;
while (async.isDone == false)
{
slider.value = async.progress;
if (async.progress == 0.9f)
{
slider.value = 1f;
async.allowSceneActivation = true;
}
yield return null;
}
}
SplashScrean.cs
void Start()
{
Invoke("loadMenuLevel", 3f);
Screen.sleepTimeout = SleepTimeout.NeverSleep;
}
void loadMenuLevel()
{
SceneManager.LoadScene("gameplay");
}
void Update()
{
if (Input.GetKey(KeyCode.Mouse0))
{
loadMenuLevel();
}
if (Input.GetKey(KeyCode.Escape))
{
Application.Quit();
}
}
This is happening because you call the Invoke("loadMenuLevel", 3f); method in the Start() method in the SplashScrean.cs script.
Start() is invoked very soon after a MonoBehaviour is attached to an object. Ostensibly, you have it attached to a GameObject in the scene. All of these are being hit at once.
You also have it as invokable via left mouse click in the same script's Update() method:
if (Input.GetKey(KeyCode.Mouse0))
{
loadMenuLevel();
}
I would add bools to each of the loading scripts that are set to true when each task completes. Then, in SplashScrean.cs, change the above condition to load the level automatically if all three are complete. Or, keep the key press condition and add the bools.
if (Application.internetReachability != NetworkReachability.NotReachable
&& loadScriptComplete && isCurrentVersion && Input.GetKey(KeyCode.Mouse0))
{
loadMenuLevel();
}
Now, if you also need the Loading MonoBehaviours to wait to execute their loading logic too, then consider adding Update() logic to them that checks the flag you set in CheckNetwork.cs.
//For example. Instead of this
private void Start()
{
Invoke("LoadAsyncOperation", 3f);
StartCoroutine(LoadAsyncOperation());
}
//Do this
private void Update()
{
//Note that we only need to check if it is a CurrentVersion, as if that is true, then we definitely have internet access.
if(CheckNetwork.IsCurrentVersion) {
Invoke("LoadAsyncOperation", 3f);
StartCoroutine(LoadAsyncOperation());
}
}
Edits/Additional Notes As I notice:
CheckNetwork.cs references CheckVersion(); in the else clause. That seems pointless. It is impossible for LoadTxtData() to have set the logic that CheckVersion() relies on in that time frame. A sort of race condition, if you will. Plus, you handle that check in LoadTxtData() anyways, so I would just do that there.
I can't give you a complete copy-pastable code here (mainy because I don't fully understand yet how all your scripts are connected between each other) but I see you are already using Coroutines.
I would as mentioned before use one central manager to handle all those things.
I will only give you a general pseudo-code here and hope you find your way frome here
public class LoadManager
{
public CheckNetwork check;
public Loading loading;
public Loading2 loading2;
public SplashScrean splashScreen;
// suprise: a not well documented fact
// you can simply make the Start method an IEnumerator (Coroutine)
// so you can directly wait for anything you want
private void IEnumerator Start()
{
// Next suprise: You can simply yield other IEnumerators
// that makes them execute and at the same time waits until they are finished
// so all methods that you want to wait for from here
// should be IEnumerators
// as said this is only an example
// you could directly within that single
// Coroutine do your complete download and check routine together
yield return check.CheckPermissions();
if(!check.isReachable)
{
yield break;
}
// same for chekcing the version
yield return check.CheckVersion();
if(!check.isUpToDate)
{
// do something for update
yield break;
}
// is up to date so start and wait for loading
yield return loading.LoadAsyncOperation();
// and so on
}
}
And there are also some builtin ways for waiting like e.g. WaitUntil, WaitForSeconds etc.

How to fix in-game double-click issue in Unity?

I have this issue with double clicking in-game. I wrote this code that checks for input and returns the type of input it received.
In editor it works just fine, but when I export the game it always returns double click type. Even if I click just once. Not sure what's causing this issue..
Below is the mouse input script and other is how I use it in other scripts.
Mouse Input script:
using System.Collections;
using UnityEngine.EventSystems;
public class MouseInput : MonoBehaviour
{
#region Variables
private enum ClickType { None, Single, Double }
private static ClickType currentClick = ClickType.None;
readonly float clickdelay = 0.25f;
#endregion
void OnEnable()
{
StartCoroutine(InputListener());
}
void OnDisable()
{
StopAllCoroutines();
}
public static bool SingleMouseClick()
{
if (currentClick == ClickType.Single)
{
currentClick = ClickType.None;
return true;
}
return false;
}
public static bool DoubleMouseClick()
{
if (currentClick == ClickType.Double)
{
currentClick = ClickType.None;
return true;
}
return false;
}
private IEnumerator InputListener()
{
while (enabled)
{
if (Input.GetMouseButtonDown(0))
{ yield return ClickEvent(); }
yield return null;
}
}
private IEnumerator ClickEvent()
{
if (EventSystem.current.IsPointerOverGameObject()) yield break;
yield return new WaitForEndOfFrame();
currentClick = ClickType.Single;
float count = 0f;
while (count < clickdelay)
{
if (Input.GetMouseButtonDown(0))
{
currentClick = ClickType.Double;
yield break;
}
count += Time.deltaTime;
yield return null;
}
}
}
Usage:
if (MouseInput.SingleMouseClick())
{
Debug.Log("Single click");
Select(true);
}
else if (MouseInput.DoubleMouseClick())
{
Debug.Log("Double click");
Select(false);
}
So on the frame Input.GetMouseButtonDown(0) evaluates to true you call into ClickEvent() which then yields on WaitForEndOfFrame(); then eventually gets back to another Input.GetMouseButtonDown(0)).
You problem is that WaitForEndOfFrame() waits until the end of the current frame:
Waits until the end of the frame after all cameras and GUI is
rendered, just before displaying the frame on screen.
It doesn't wait until the next frame. Because of this, all values returned by the Input API are still going to be the same. You want a yield return null instead of WaitForEndOfFrame().

Run more than expected number

Please do not get too hard about my grammar.
I write follow class for delay
public class Queue_System_Of_Begin_Game : MonoBehaviour
{
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Game_Controller.Player1_First_throws_true && Game_Controller.Player2_First_throws_true)
{
StartCoroutine(ExecuteAfterTime(1));
}
}
//--------------------------------------
public GameObject player1_icon, player2_icon, dice1_p1, dice2_p1, dice1_p2, dice2_p2;
void determine_the_turn()
{
Debug.Log("update");
}
IEnumerator ExecuteAfterTime(float time)
{
yield return new WaitForSeconds(time);
determine_the_turn();
}
}
I receive 62 times the word update on the console.
This problem will cause my next round of games to run 62 times, which slowed down my game.
The Update() method is called once per frame, thats the reason you get 62 "updates".
You can try adding a boolean so it only gets called once like this:
bool ischecked = false;
void Update(){
if (!ischecked){
if (Game_Controller.Player1_First_throws_true && Game_Controller.Player2_First_throws_true) {
ischecked = true;
StartCoroutine (ExecuteAfterTime (1));
}
}
}
I find solution for my problem .I must use a boolean variable in my if commond like this :
public class Queue_System_Of_Begin_Game : MonoBehaviour
{
private bool coroutineStarted;
// Update is called once per frame
void Update()
{
if (!coroutineStarted && Game_Controller.Player1_First_throws_true && Game_Controller.Player2_First_throws_true)
{
coroutineStarted = true ;
StartCoroutine(ExecuteAfterTime(1));
}
}
//--------------------------------------
public GameObject player1_icon, player2_icon, dice1_p1, dice2_p1, dice1_p2, dice2_p2;
void determine_the_turn()
{
Debug.Log("update");
}
IEnumerator ExecuteAfterTime(float time)
{
yield return new WaitForSeconds(time);
determine_the_turn();
}
}

Unity3D C# not going to the else statement (Invoke Repeating)

I'm currently making my first game using Unity3D written in C#. I've faced some game bug just now, and it's been hours that I've been thinking of what might have been wrong with my code, but I can't see anything wrong.
I have a stat called healthRegen which add HP to the character every second. What I have noticed is even when my player HP drops to zero, it just keep adding HP to my player, thus making it alive again. I have a method that checks if my character HP drops to zero but it didn't call it and I don't know why.
public bool fullHealth() {
if(currentHealth >= maxHealth) {
return true;
} else {
return false;
}
}
public void adjustHealth() {
if(currentHealth > maxHealth) {
currentHealth = maxHealth;
}
if(currentHealth < 0) {
currentHealth = 0;
}
}
That is my method and this is my player script
void Start() {
InvokeRepeating("regenerate", 0f, 1f);
}
// Check if the player is still alive
public bool isDead() {
if (attribute.currentHealth == 0) {
return true;
} else {
return false;
}
}
// Die method
public void dead() {
animation.Play(dieClip.name);
}
private void regenerate() {
if (!attribute.fullHealth()) {
attribute.currentHealth += attribute.healthRegen;
} else {
attribute.adjustHealth();
}
}
This maybe a dumb question for others but I'm sorry, I don't know what to do anymore.
Looks like your problem is that regenerate() is adding health to your player if his health is at 0.
You just need to add a call to isDead() to prevent this.
private void regenerate() {
if (!attribute.fullHealth() && !attribute.isDead()) {
attribute.currentHealth += attribute.healthRegen;
} else {
attribute.adjustHealth();
}
}
I just found out a solution right now, I forgot the CancelInvoke() method
private void regenerate() {
if (!attribute.fullHealth()) {
attribute.currentHealth += attribute.healthRegen;
} else {
CancelInvoke("regenerate");
attribute.adjustHealth();
}
}
Now it's working perfectly!

Categories