Make GUI.Box stay for 3 seconds - c#

I'm working on a project in Unity using C# scripting. The GUI.Box will appear at the top the screen. The box will disappear when the player leaves the spot. How can I make the box stay there for an additional 3 seconds after the player leaves the designated spot?
Danpe's code corrected (working code):
bool shown = false;
void OnGUI () {
if (car.transform.position.y>=43 && car.transform.position.y<=44)
{
shown = true;
}
else if (shown)
{
StartCoroutine(DisapearBoxAfter(3.0f));
}
if(shown)
{
GUI.Box(new Rect((Screen.width/2)-200,0,400,30) , "King of the hill");
}
}
IEnumerator DisapearBoxAfter(float waitTime) {
// suspend execution for waitTime seconds
yield return new WaitForSeconds(waitTime);
shown = false;
}
void Update () {
OnGUI ();
}

bool shown = false;
void OnGUI () {
if (car.transform.position.y>=43 && car.transform.position.y<=44)
{
shown = true;
}
else if (shown)
{
StartCoroutine(DisapearBoxAfter(3.0));
}
if(shown) {
GUI.Box(new Rect((Screen.width/2)-200,0,400,30) , "King of the hill");
}
}
IEnumerator DisapearBoxAfter(float waitTime) {
// suspend execution for waitTime seconds
return yield WaitForSeconds (waitTime);
shown = false;
}
void Update () {
OnGUI ();
}

function Start ()
{
ShowBox ();
}
function ShowBox ()
{
// show label
show = true;
// cancel invoking method if already set to call after 3 seconds
CancelInvoke("HideBox");
// will call HideBox () after 3 sec
Invoke ("HideBox", 3.0F);
}
function HideBox ()
{
// dont show label
show = false;
}
function Update ()
{
if (car.transform.position.y>=43 && car.transform.position.y<=44)
{
ShowBox ();
}
}
function OnGUI ()
{
if(shown)
{
GUI.Box(new Rect((Screen.width/2)-200,0,400,30) , "King of the hill");
}
}

Related

Ending a coroutine while running in unity3d

Im trying to start and end a coroutine with a button. I can start the coroutine but I cant stop it, if I click the button again after the first time starting the coroutine it just restarts again and the slider value goes up.
heres my code
public void LoopButton(){
if (lb == 1){
StopCoroutine (AutoLoop());
tb--;
} else {
StartCoroutine (AutoLoop ());
tb++;
}
}
IEnumerator AutoLoop(){
slider.value = slider.minValue;
while(slider.value < slider.maxValue){
slider.value++;
yield return new WaitForSeconds(0.5f);
}
StartCoroutine (AutoLoop());
}
You need to call StopCoroutine with a reference to the same Coroutine returned by StartCoroutine, like this:
private Coroutine loopCoroutine;
public void LoopButton()
{
if (lb == 1)
{
StopCoroutine(loopCoroutine);
tb--;
}
else
{
loopCoroutine = StartCoroutine(AutoLoop());
tb++;
}
}
To use this approach, change your AutoLoop method to use a while loop rather than a starting another AutoLoop coroutine at the end of the method. Otherwise, you will not be able to stop this new coroutine that is started at the end of AutoLoop.
IEnumerator AutoLoop()
{
while(true)
{
slider.value = slider.minValue;
while (slider.value < slider.maxValue)
{
slider.value++;
yield return new WaitForSeconds(0.5f);
}
}
}
For an alternate solution, as another user commented, it's also possible to stop the coroutine via a boolean flag:
private bool stopLoop;
public void LoopButton()
{
if (lb == 1)
{
stopLoop = true;
tb--;
}
else
{
stopLoop = false;
StartCoroutine (AutoLoop ());
tb++;
}
}
IEnumerator AutoLoop()
{
slider.value = slider.minValue;
while (slider.value < slider.maxValue && !stopLoop)
{
slider.value++;
yield return new WaitForSeconds(0.5f);
}
if (!stopLoop)
{
StartCoroutine(AutoLoop());
}
}
However, using Unity's StopCoroutine is preferable to using a boolean flag for readability & cleanliness.
You can use StopCoroutine.
More info here:
https://docs.unity3d.com/ScriptReference/MonoBehaviour.StopCoroutine.html
Use string for coroutine name. Like this:
public void LoopButton(){
if (lb == 1){
StopCoroutine ("AutoLoop");
tb--;
} else {
StartCoroutine ("AutoLoop");
tb++;
}
}
IEnumerator AutoLoop(){
slider.value = slider.minValue;
while(slider.value < slider.maxValue){
slider.value++;
yield return new WaitForSeconds(0.5f);
}
StartCoroutine ("AutoLoop");
}

Show Result Unity ADS rewards

When you pause the game, there is a button where you can click and view a non-skippable video at the end of this video. I want to implement the following:
If the video is watched to the end they gain 1 extra heart (max 3 full hearts) and get a chat box or alert dialog thanking.
If the video does not open, fully load or something goes wrong, they get nothing and a chat box or alert dialog appears.
Currently the video loads, But when the video ends, the prize is not received (1 extra heart), what is wrong in my code?
Below are the button ads
using UnityEngine;
using System.Collections;
using UnityEngine.Advertisements;
using UnityEngine.UI;
public class Ads : MonoBehaviour {
public Button getAds;
private Hearts heart;
void OnEnable ()
{
getAds.onClick.AddListener (() => GetAds (getAds));
}
private void GetAds ( Button buttonPressed)
{
if (buttonPressed == getAds) {
Advertisement.Initialize ("XXXXXX", true);
Advertisement.IsReady ("rewardedVideo");
Advertisement.Show ("rewardedVideo");
}
}
public void HandleShowResult (ShowResult result)
{
switch (result)
{
case ShowResult.Finished:
heart = GameObject.FindGameObjectWithTag ("Hearts").GetComponent<Hearts> () as Hearts;
heart.AddHeart ();
break;
case ShowResult.Skipped:
Debug.Log("The ad was skipped before reaching the end.");
break;
case ShowResult.Failed:
Debug.LogError("The ad failed to be shown.");
break;
}
}
void OnDisable ()
{
getAds.onClick.RemoveAllListeners ();
}
}
Below current hearts system script
using UnityEngine;
using System.Collections;
public class Hearts : MonoBehaviour {
public Texture2D[]initialHeart;
private int hearts;
private int currentHearts;
void Start () {
GetComponent<GUITexture>().texture = initialHeart[0];
hearts = initialHeart.Length;
}
void Update () {
}
public bool TakeHeart()
{
if (hearts < 0) {
return false;
}
if (currentHearts < (hearts - 1)) {
currentHearts += 1;
GetComponent<GUITexture> ().texture = initialHeart [currentHearts];
return true;
} else {
return false;
}
}
public bool AddHeart() {
if (currentHearts > 0) {
currentHearts -= 1;
GetComponent<GUITexture> ().texture = initialHeart [currentHearts];
return true;
} else {
return false;
}
}
}
Your code is missing the most important part which is
ShowOptions options = new ShowOptions();
options.resultCallback = HandleShowResult;
Advertisement.Show(zoneId, options);
Without it, HandleShowResult wont be called and you won't know what happened after ad has been displayed. Also score wont increment either. I went ahead to implement a coroutine which makes sure that everything is fine before displaying ads. This is was not tested but any problem can easily be fixed. Errors are displayed with red color. Green means success.
public class Ads : MonoBehaviour
{
public string gameId;
public string zoneId;
public Button getAds;
private Hearts heart;
void OnEnable()
{
getAds.onClick.AddListener(() => GetAds(getAds));
}
private void GetAds(Button buttonPressed)
{
if (buttonPressed == getAds)
{
//Wait for ad to show. The timeout time is 3 seconds
StartCoroutine(showAdsWithTimeOut(3));
}
}
public void HandleShowResult(ShowResult result)
{
switch (result)
{
case ShowResult.Finished:
heart = GameObject.FindGameObjectWithTag("Hearts").GetComponent<Hearts>() as Hearts;
heart.AddHeart();
Debug.Log("<color=green>The ad was skipped before reaching the end.</color>");
break;
case ShowResult.Skipped:
Debug.Log("<color=yellow>The ad was skipped before reaching the end.</color>");
break;
case ShowResult.Failed:
Debug.LogError("<color=red>The ad failed to be shown.</color>");
break;
}
}
IEnumerator showAdsWithTimeOut(float timeOut)
{
//Check if ad is supported on this platform
if (!Advertisement.isSupported)
{
Debug.LogError("<color=red>Ad is NOT supported</color>");
yield break; //Exit coroutine function because ad is not supported
}
Debug.Log("<color=green>Ad is supported</color>");
//Initialize ad if it has not been initialized
if (!Advertisement.isInitialized)
{
//Initialize ad
Advertisement.Initialize(gameId, true);
}
float counter = 0;
bool adIsReady = false;
// Wait for timeOut seconds until ad is ready
while(counter<timeOut){
counter += Time.deltaTime;
if( Advertisement.IsReady (zoneId)){
adIsReady = true;
break; //Ad is //Ad is ready, Break while loop and continue program
}
yield return null;
}
//Check if ad is not ready after waiting
if(!adIsReady){
Debug.LogError("<color=red>Ad failed to be ready in " + timeOut + " seconds. Exited function</color>");
yield break; //Exit coroutine function because ad is not ready
}
Debug.Log("<color=green>Ad is ready</color>");
//Check if zoneID is empty or null
if (string.IsNullOrEmpty(zoneId))
{
Debug.Log("<color=red>zoneId is null or empty. Exited function</color>");
yield break; //Exit coroutine function because zoneId null
}
Debug.Log("<color=green>ZoneId is OK</color>");
//Everything Looks fine. Finally show ad (Missing this part in your code)
ShowOptions options = new ShowOptions();
options.resultCallback = HandleShowResult;
Advertisement.Show(zoneId, options);
}
void OnDisable()
{
getAds.onClick.RemoveAllListeners();
}
}

A pause menu using a button and click

I'd like to know how I can pause and unpause menu from the same button using the mouse pointer when I click on it.
Lets say I have this. C#
void Update () {
if (Button_Pause.OnPointerClick()) {
if(!active){
PauseGame();
}
else{
ResumeGame();
}
active = !active;
}
}
public void PauseGame()
{
Button_Pause = Button_Pause.GetComponent<Button> ();
Canvas_PauseMenu.enabled = true;
Button_Exit.enabled = true;
Button_Pause.enabled = true;
}
public void ResumeGame()
{
Canvas_PauseMenu.enabled = false;
Button_Exit.enabled = false;
Button_Pause.enabled = false;
}
In the first line, where I call the OnPointerClick I'm just guessing because I don't know what to do. What I've searched around, using click to show something it's having a TimeScale or something like that.
¿Can anyone help moi? Please.
Add a listener for your button and in your pause script set timescale to zero to pause the game
[SerializeField] private Button MyButton = null; // assign in the editor
void Start() { MyButton.onClick.AddListener(() => { pause();});
}
void pause(){
if (Time.timeScale == 1)
{
Time.timeScale = 0;
}
else
{
Time.timeScale = 1;
}
}
I managed to solve the issue. It might not be efficient but it does what I need.
I created 2 buttons in the same place. Those buttons are represented with different sprites (Pause & Play). "Pause" is visible since the start. When I click on it, the menu pops up, the "Pause" stop being active and the "Play" sprite button activates and pops up too. When I click on it, I unpause and goes back to the "Pause" sprite visible in the screen.
void Start () {
Canvas_PauseMenu = Canvas_PauseMenu.GetComponent<Canvas> ();
Button_Pause = Button_Pause.GetComponent<Button> ();
Button_Resume = Button_Resume.GetComponent<Button> ();
Canvas_PauseMenu.enabled = false;
Button_Resume.enabled = false;
Button_Resume.gameObject.SetActive (false);
}
// Update is called once per frame
public void PauseTest () {
if(!active){
PauseGame();
}
else{
ResumeGame();
}
}
public void BackToMainMenu()
{
Application.LoadLevel (0);
}
public void PauseGame()
{
Canvas_PauseMenu.enabled = true;
Button_Exit.enabled = true;
Button_Pause.enabled = false;
Button_Pause.gameObject.SetActive (false);
Button_Resume.enabled = true;
Button_Resume.gameObject.SetActive (true);
active = true;
Time.timeScale = 0;
}
public void ResumeGame()
{
Canvas_PauseMenu.enabled = false;
Button_Exit.enabled = false;
Button_Pause.enabled = true;
Button_Pause.gameObject.SetActive (true);
Button_Resume.enabled = false;
Button_Resume.gameObject.SetActive (false);
active = false;
Time.timeScale = 1;
}

C# lambda call in for loop references to last object

I've researched this problem and I've tried fixing the code as instructed in a few questions before (such as this one) but it still won't work. Though I must say that all the answers I checked were from 2009-2010 so they might be obsolete.
This is the culprit code:
foreach(Entity player in players)
{
if(player.actions.Count > 0)
{
Entity temp = player;
player.isDoingAction = true;
Debug.Log(player.name + " started action");
player.actions.Dequeue().Execute(() => { temp.isDoingAction = false; Debug.Log(temp.name + " finished"); });
}
}
This prints the following:
Player1 started action
Player2 started action
Player2 finished
Player2 finished
When it should print:
Player1 started action
Player2 started action
Player1 finished
Player2 finished
Or something similar.
This code runs in a Unity coroutine function.
A bit larger snip from the code:
GameManager.cs
private IEnumerator RunTurn()
{
...
...
...
for(int i = 0; i < phases; i++)
{
//Assign action to each player
foreach(Entity player in players)
{
if(player.actions.Count > 0)
{
Entity temp = player;
player.isDoingAction = true;
Debug.Log(player.name + " started action");
player.actions.Dequeue().Execute(() => { temp.isDoingAction = false; Debug.Log(temp.name + " finished"); });
}
}
//Wait for each player to finish action
foreach(Entity player in players)
{
while(player.isDoingAction == true)
{
Debug.Log("Waiting for " + player.name);
yield return null;
}
}
}
...
...
...
}
Action.cs
public override void Execute(System.Action callback)
{
Move(callback);
}
private void Move(System.Action callback)
{
...
...
...
//Move target entity
target.MoveToPosition(newPosition, mSpeed, callback);
target.location = newLocation;
...
...
...
}
Entity.cs
public void MoveToPosition(Vector3 position, float speed, System.Action callback)
{
StartCoroutine(CoMoveToPosition(position, speed, callback));
}
//Move to position
private IEnumerator CoMoveToPosition(Vector3 position, float speed, System.Action callback)
{
while(position != transform.position)
{
transform.position = Vector3.MoveTowards(transform.position, position, speed * Time.deltaTime);
yield return null;
}
//Move finished so use callback
callback();
}
Solution
It turns out there is a bug in Unity with coroutines and anonymous lambda callbacks. Check this link for more.
Working piece of code:
foreach(Entity player in players)
{
if(player.actions.Count > 0)
{
player.isDoingAction = true;
Debug.Log(player.name + " started action");
System.Func<Entity, System.Action> action = new System.Func<Entity,System.Action>(p =>
new System.Action(() => { p.isDoingAction = false; Debug.Log(p.name + " finished"); }));
player.actions.Dequeue().Execute(action(player));
}
}
You can capture the value the following way:
var action = new Func<Entity, Action>(p =>
new Action(() => { p.isDoingAction = false; Debug.Log(p.name + " finished")); })(player);
player.actions.Dequeue().Execute(action);
Try
player.actions.Dequeue().Execute(temp =>
{ temp.isDoingAction = false;
Debug.Log(temp.name + " finished");
});

Trouble with loading new scene after a certain amount of time

I'm trying to load a new scene after clicking on the GUI.Button, and is working, but i want the level to load after a certain amount of time has passed, and my code is not working, what should i change or add to the script?
void OnGUI () {
GUI.skin = zenzaiSkin;
if(GUI.Button(new Rect((Screen.width/2-185),560,370,80), "START")) {
loadLevel1 = true;
audio.Play ();
}
}
public IEnumerator loadLevel() {
if(loadLevel1 = true){
yield return new WaitForSeconds(secondsWaiting);
Application.LoadLevel(1);
}
}
Thanks in advance
well did you call the function ? i can't see the call... just add
StartCoroutine(loadLevel());
under audio.play
so your code should look like something like this
void OnGUI () {
GUI.skin = zenzaiSkin;
if(GUI.Button(new Rect((Screen.width/2-185),560,370,80), "START")) {
loadLevel1 = true;
audio.Play ();
StartCoroutine(loadLevel());
}
}

Categories

Resources