I am making a badminton simulator in unity, where the opponent is a set of video clips. I am trying to add some delay to my update method so theres some time between two clips of the opponent. However this delay only applies to the video clips and not the shuttle that arrives from behind the video.
My Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Video;
public class Video_Player : MonoBehaviour
{
public VideoPlayer activeCam, otherCam;
public List<VideoClip> playlist = new List<VideoClip>();
public GameObject shuttle;
VideoClip nextClip;
private bool Timer;
void Start()
{
Shuffle(playlist);
// play the first video in the playlist
PrepareNextPlaylistClip();
SwitchCams(activeCam);
Timer=false;
// setup an event to automatically call SwitchCams() when we finish playing
activeCam.loopPointReached += SwitchCams;
otherCam.loopPointReached += SwitchCams;
shuttle.SetActive(false);
}
void Update()
{
if (playlist.Count == 0)
return;
if(!Timer)
{
StartCoroutine(CountDown(5));
if (nextClip == null && activeCam.time >= activeCam.clip.length - 0.1)
{
PrepareNextPlaylistClip();
shuttle.SetActive(false);
}
if(activeCam.time >= 1.0f && activeCam.time <= 2.95f)
{
Debug.Log("start:"+activeCam.time);
shuttle.SetActive(true);
}
else
//if(activeCam.time >= 2.95f || activeCam.time <= 1.0f)
{
Debug.Log("end:"+activeCam.time);
shuttle.SetActive(false);
}
}
}
void SwitchCams(VideoPlayer thisCam)
{
activeCam = otherCam;
otherCam = thisCam;
activeCam.targetCameraAlpha = 1f;
otherCam.targetCameraAlpha = 0f;
Debug.Log("new clip: " + nextClip.name);
nextClip = null;
}
void PrepareNextPlaylistClip()
{
nextClip = playlist[0];
otherCam.clip = nextClip;
otherCam.Play();
playlist.RemoveAt(0);
}
//delay couroutine
IEnumerator CountDown(float delay)
{
Timer = true;
yield return new WaitForSeconds(delay);
Timer= false;
}
// randomize the video playlist
public static void Shuffle<T>(IList<T> playlist)
{
int n = playlist.Count;
while (n > 1)
{
n--;
int k = Random.Range(0, n);
T value = playlist[k];
playlist[k] = playlist[n];
playlist[n] = value;
}
}
}
Forgive me if I'm misunderstanding your code but rather than having it all in Update() couldn't you just have it in an IEnumerator like this?
void Start()
{
Shuffle(playlist);
// play the first video in the playlist
PrepareNextPlaylistClip();
SwitchCams(activeCam);
activeCam.loopPointReached += SwitchCams;
otherCam.loopPointReached += SwitchCams;
shuttle.SetActive(false);
//Run the function on start
StartCoroutine(Function());
}
IEnumerator Function()
{
while(true)
{
if(playlist.Count == 0)
{
//If you have no clips left exit out of the loop
break;
}
if(nextClip == null)
{
//If you have clips left load the next clip
shuttle.SetActive(false);
PrepareNextPlaylistClip();
}
yield return new WaitForSeconds(1); //This is your delay
//Execute the code you want to run after the delay here
}
}
Related
I have the following coroutine which displays a warning error image when you click on a button and you do not have enough money:
public IEnumerator ShowWarning()
{
if (warningActive)
yield break;
Debug.Log("started...");
warningActive = true;
NotEnoughMoneyImage.SetActive(true);
yield return new WaitForSeconds(1f);
NotEnoughMoneyImage.SetActive(false);
warningActive = false;
Debug.Log("ended...");
yield break;
}
NotEnoughMoneyImage is a public Image GameObject
warningActive is a public bool
It shows the debug line "started" but never shows the debug line "ended". How is that possible? I call it from another script, but I don't think that there is the problem.
Here I've got the first script which is attached to an empty object (the main parent of the shop UI). This script is the main one. At the final of it appears the IEnumerator:
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading;
using UnityEngine;
public class WeaponShop : MonoBehaviour
{
public static WeaponShop shop;
public GameObject itemHolderPrefab;
public Transform grid;
public List<Weapon> weaponList = new List<Weapon>();
private List<GameObject> itemHolderList = new List<GameObject>();
public List<GameObject> buyButtonList = new List<GameObject>();
public GameObject NotEnoughMoneyImage;
public bool warningActive = false;
void Start()
{
NotEnoughMoneyImage.SetActive(false);
shop = this;
FillList();
}
void FillList()
{
for (int i = 0; i < weaponList.Count; ++i)
{
GameObject holder = Instantiate(itemHolderPrefab, grid);
ItemHolder holderScript = holder.GetComponent<ItemHolder>();
holderScript.itemID = weaponList[i].weaponID;
holderScript.itemName.text = weaponList[i].weaponName;
if (weaponList[i].weaponPrice != (int)weaponList[i].weaponPrice)
holderScript.itemPrice.text = weaponList[i].weaponPrice.ToString("N2") + "$";
else
holderScript.itemPrice.text = weaponList[i].weaponPrice.ToString() + "$";
holderScript.itemSprite.sprite = weaponList[i].weaponSprite;
holderScript.buyButton.GetComponent<BuyButton>().weaponID = weaponList[i].weaponID;
itemHolderList.Add(holder);
buyButtonList.Add(holderScript.buyButton);
}
}
public void SoldOutText(int weaponID)
{
for (int i = 0; i < weaponList.Count; ++i)
if (weaponList[i].weaponID == weaponID)
itemHolderList[i].GetComponent<ItemHolder>().itemPrice.text = "SOLD OUT!";
}
public IEnumerator ShowWarning()
{
if (warningActive)
yield break;
Debug.Log("started...");
warningActive = true;
NotEnoughMoneyImage.SetActive(true);
yield return new WaitForSeconds(1f);
NotEnoughMoneyImage.SetActive(false);
warningActive = false;
Debug.Log("ended...");
yield break;
}
}
Now the second script is attached to the "BUY" button of a prefab "Item Holder". The script above (first one) generates a number of prefabs "item holder" and fills them with the info from the inspector (weapon name, price, stats etc). Here it comes the second script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.PlayerLoop;
public class BuyButton : MonoBehaviour
{
public int weaponID;
public TMP_Text buttonText;
public void BuyWeapon()
{
if (weaponID == 0)
{
Debug.Log("Weapon ID is ZERO");
return;
}
for (int i = 0; i < WeaponShop.shop.weaponList.Count; ++i)
{
if (WeaponShop.shop.weaponList[i].weaponID == weaponID && !WeaponShop.shop.weaponList[i].isBought && CurrencyManager.currencyManager.RequestMoney(WeaponShop.shop.weaponList[i].weaponPrice))
{// USING
WeaponShop.shop.weaponList[i].isBought = true;
CurrencyManager.currencyManager.ReduceMoney(WeaponShop.shop.weaponList[i].weaponPrice);
WeaponShop.shop.SoldOutText(weaponID);
UpdateBuyButton();
// change the weapon system and update your weapon, depending on ID / name here
}
else if (WeaponShop.shop.weaponList[i].weaponID == weaponID && !WeaponShop.shop.weaponList[i].isBought && !CurrencyManager.currencyManager.RequestMoney(WeaponShop.shop.weaponList[i].weaponPrice))
{//NOT ENOUGH MONEY. I know that it enters this if statement because of the debug.log
Debug.Log("Corountine...");
StartCoroutine(WeaponShop.shop.ShowWarning());
}
else if (WeaponShop.shop.weaponList[i].weaponID == weaponID && WeaponShop.shop.weaponList[i].isBought)
{
UpdateBuyButton();
// change the weapon system and update your weapon, depending on ID / name here
}
}
}
void UpdateBuyButton()
{
buttonText.text = "USING";
for (int i = 0; i < WeaponShop.shop.buyButtonList.Count; ++i)
{
BuyButton buyButtonScript = WeaponShop.shop.buyButtonList[i].GetComponent<BuyButton>();
for (int j = 0; j < WeaponShop.shop.weaponList.Count; ++j)
{
if (WeaponShop.shop.weaponList[j].weaponID == buyButtonScript.weaponID && WeaponShop.shop.weaponList[j].isBought && WeaponShop.shop.weaponList[j].weaponID != weaponID)
{
buyButtonScript.buttonText.text = "USE";
}
}
}
// change the weapon system and update your weapon, depending on ID / name
}
}
At line 34 is the start coroutine method.
Yes, I figured it out! When I was entering the shop UI, I was pausing the game in another script. So the time scale was 0. All I had to do was to replace in the coroutine this line:
yield return new WaitForSeconds(1f);
to this one:
yield return new WaitForSecondsRealtime(1f);
That was it! Now it works just fine! For everyone which thinks the coroutine does not work well, check your time scale. It's very important (Idk how I missed it!).
I want there to be 15 minutes between the rewarded ads. I made this:
When you see the ad:
public void HandleUserEarnedReward(object sender, Reward args)
{
DateTime ad= DateTime.Now.AddMinutes(15);
long adTicks = ad.Ticks;
PlayerPrefs.SetInt("ticksVideo", (int)adTicks); }
Countdown:
void Update(){
DateTime currentTime= DateTime.Now;
long currentTicks= currentTime.Ticks;
PlayerPrefs.SetInt("currentTicks", (int)currentTicks);
TimerControl = PlayerPrefs.GetInt("ticksVideo") - PlayerPrefs.GetInt("currentTicks");
string mins = ((int)TimerControl / 600000000).ToString("00"); //600.000.000 ticks per minute
string segs = ((int)TimerControl % 600000000).ToString("00");
TimerString = string.Format("{00}:{01}", mins, segs);
GetComponent<Text>().text = TimerString; }
In DateTime.Now.AddMinutes I enter 15 but the countdown lasts about 50 seconds. On the other hand, the TimerString also does not show the format that I indicate. What's wrong? Should I use TimeSpan?
Edit:
I have 2 classes:
The player watch the ad:
public class AdMob : MonoBehaviour
{
public static bool video = false;
public Button buttonAd;
public GameObject countdown;
public void HandleUserEarnedReward(object sender, Reward args)
{
//Rewards
buttonAd.interactable = false;
countdown.SetActive(true);
video = true;
}
}
The countdown begins:
public class CountdownAd : MonoBehaviour
{
public static float timeSinceLastAd = 0;
void Update(){
if (AdMob.video)
{
timeSinceLastAd += Time.deltaTime;
if (timeSinceLastAd > (60 * 15))
{
buttonAd.interactable = true;
countdown.SetActive(false);
timeSinceLastAd = 0;
AdMob.video = false;
}
} else
{
timeSinceLastAd = 0;
}
}}
EDIT 2:
public class AdMob : MonoBehaviour {
public GameObject countdownGameObject;
public Button adButton;
public Text countdown;
//I hit the adButton and I watch the rewarded ad...
public void HandleUserEarnedReward(object sender, Reward args)
{
//Rewards..
countdownGameObject.SetActive(true);
StartCoroutine(timer(15));
adButton.interactable = false;
}
IEnumerator timer(int lapse)
{
while (lapse > 0)
{
int seconds = lapse % 60;
int minutes = lapse / 60;
countdown.text = $"{lapse / 60: 00}:{lapse % 60:00}";
yield return new WaitForSeconds(1f);
lapse--;
}
countdown.text = "00:00";
//CountDown Finished
gameObject.SetActive(false);
if (lapse == 0)
{
adButton.interactable = true;
countdownGameObject.SetActive(false);
}
}
}
its a sample of countdown linked to the UIText displaying the countdown, i am using a StartCoroutine launched from Start() and dont use Update()
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class Countdown : MonoBehaviour
{
private Coroutine coroutine;
private Text UITimer;
void Start()
{
UITimer = GetComponent<Text>();
if (coroutine != null) StopCoroutine(coroutine);
coroutine = StartCoroutine(timer(60*15));
}
IEnumerator timer(int lapse)
{
while (lapse > 0)
{
UITimer.text = $"{lapse / 60:00}:{lapse % 60:00}";
yield return new WaitForSeconds(1f);
lapse--;
}
UITimer.text = "00:00";
//CountDown Finished
gameObject.SetActive(false);
// and all other things
}
}
Your code looks confusing, keep a timer and increment it with Time.delaTime each frame, when the timer is > 15 minutes play ad and reset timer.
float timeSinceLastAd = 0;
void Update(){
timeSinceLastAd += Time.deltaTime;
if (timeSinceLastAd > (60 * 15)) {
PlayAd(); //or whatever your method to play an ad is called
timeSinceLastAd = 0;
}
}
So I downloaded the assets file of the final version of the Sloan Kelly Card Game Project from YouTube Link to the video where you can download it in the description and I was playing around with the game it was all working fine.
I then re-opened the game the project the next day and the 'Stick Button' is no longer working. It appears on screen and appears intractable but it will not press.
I made a couple of changes to the code to make it easier to read but didn't think i had changed anything to do with this button. The rest of the code runs fine just this one button.
I'm unsure how to link my project assets package on this sight, but I can show you the code for the button and was just wondering if anyone can see the problem straight away / could you tell me how to link the whole file so you can get a better look at what is happening.
Thanks in advance
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class GameController : MonoBehaviour
{
int dealersFirstCard = -1;
public CardStack player;
public CardStack dealer;
public CardStack deck;
public Button hitButton;
public Button stickButton;
public Button playAgainButton;
public Text winnerText;
/*
* Cards dealt to each player
* First player hits/sticks/bust
* Dealer's turn unyil minimum of 17 value in hand
* Dealers cards: first card is hidden, other cards are facing
*/
#region Hit
public void Hit()
{
player.Push(deck.Pop());
if (player.HandValue() > 21)
{
hitButton.interactable = false;
stickButton.interactable = false;
StartCoroutine(DealersTurn());
}
}
#endregion
#region Stick
public void Stick()
{
hitButton.interactable = false;
stickButton.interactable = false;
StartCoroutine(DealersTurn());
}
#endregion
#region Play Again
public void PlayAgain()
{
playAgainButton.interactable = false;
player.GetComponent<CardStackView>().Clear();
dealer.GetComponent<CardStackView>().Clear();
deck.GetComponent<CardStackView>().Clear();
deck.CreateDeck();
winnerText.text = "";
hitButton.interactable = true;
stickButton.interactable = true;
dealersFirstCard = -1;
StartGame();
}
#endregion
void Start()
{
StartGame();
}
void StartGame()
{
for (int i = 0; i < 2; i++)
{
player.Push(deck.Pop());
HitDealer();
}
}
void HitDealer()
{
int card = deck.Pop();
if (dealersFirstCard < 0)
{
dealersFirstCard = card;
}
dealer.Push(card);
if (dealer.CardCount >= 2)
{
CardStackView view = dealer.GetComponent<CardStackView>();
view.Toggle(card, true);
}
}
#region Dealers turn
IEnumerator DealersTurn()
{
hitButton.interactable = false;
stickButton.interactable = false;
CardStackView view = dealer.GetComponent<CardStackView>();
view.Toggle(dealersFirstCard, true);
view.ShowCards();
yield return new WaitForSeconds(1f);
while (dealer.HandValue() < 17)
{
HitDealer();
yield return new WaitForSeconds(1f);
}
if (player.HandValue() > 21 || (dealer.HandValue() >= player.HandValue() && dealer.HandValue() <= 21))
{
winnerText.text = "You Lose!";
}
else if (dealer.HandValue() > 21 || (player.HandValue() <= 21 && player.HandValue() > dealer.HandValue()))
{
winnerText.text = "You Win!";
}
else
{
winnerText.text = "The house wins!";
}
yield return new WaitForSeconds(1f);
playAgainButton.interactable = true;
}
#endregion
}
thanks for reading.
I'm currently building a small memory card game in Unity using C#. I have the main portion of code finished but when I press the play button on a certain scene Unity freezes.
I believe it is due to an infinite While loop, but I can not find the issue. I would really appreciate any help anyone can offer. I will leave my code below. Thanks in advance.
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using UnityEngine;
public class Pairs : MonoBehaviour {
public Sprite[] face; //array of card faces
public Sprite back;
public GameObject[] deck; //array of deck
public Text pairsCount;
private bool deckSetUp = false;
private int pairsLeft = 13;
// Update is called once per frame
void Update () {
if (!deckSetUp)
{
SetUpDeck();
}
if (Input.GetMouseButtonUp(0)) //detects left click
{
CheckDeck();
}
}//Update
void SetUpDeck()
{
for (int ix = 0; ix < 2; ix++)
{
for(int i = 1; i < 14; i++)//sets up card value (2-10 JQKA)
{
bool test = false;
int val = 0;
while (!test)
{
val = Random.Range(0, deck.Length);
test = !(deck[val].GetComponent<Card>().SetUp);
}//while
//sets up cards
deck[val].GetComponent<Card>().Number = i;
deck[val].GetComponent<Card>().SetUp = true;
}//nested for
}//for
foreach (GameObject crd in deck)
{
crd.GetComponent<Card>().setUpArt();
}
if (!deckSetUp)
{
deckSetUp = true;
}
}//SetUpDeck
public Sprite getBack()
{
return back;
}//getBack
public Sprite getFace(int i)
{
return face[i - 1];
}//getFace
void CheckDeck()
{
List < int > crd = new List<int>();
for(int i = 0; i < deck.Length; i++)
{
if(deck[i].GetComponent<Card>().State == 1)
{
crd.Add(i);
}
}
if(crd.Count == 2)
{
CompareCards(crd);
}
}//CheckDeck
void CompareCards(List<int> crd)
{
Card.NO_TURN = true; //stops cards turning
int x = 0;
if(deck[crd[0]].GetComponent<Card>().Number ==
deck[crd[1]].GetComponent<Card>().Number)
{
x = 2;
pairsLeft--;
pairsCount.text = "PAIRS REMAINING: " + pairsLeft;
if(pairsLeft == 0) // goes to home screen when game has been won
{
SceneManager.LoadScene("Home");
}
}
for(int j = 0; j < crd.Count; j++)
{
deck[crd[j]].GetComponent<Card>().State = x;
deck[crd[j]].GetComponent<Card>().PairCheck();
}
}//CompareCards
}
I believe the issue lies in the while(!test) but i do not know why test never become true.
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
public class Card : MonoBehaviour {
public static bool NO_TURN = false;
[SerializeField]
private int cardState; //state of card
[SerializeField]
private int cardNumber; //Card value (1-13)
[SerializeField]
private bool _setUp = false;
private Sprite back; //card back (Green square)
private Sprite face; //card face (1-10 JQKA)
private GameObject pairsManager;
void Begin()
{
cardState = 1; //cards face down
pairsManager = GameObject.FindGameObjectWithTag("PairsManager");
}
public void setUpArt()
{
back = pairsManager.GetComponent<Pairs>().getBack();
face = pairsManager.GetComponent<Pairs>().getFace(cardNumber);
turnCard();//turns the card
}
public void turnCard() //handles turning of card
{
if (cardState == 0)
{
cardState = 1;
}
else if(cardState == 1)
{
cardState = 0;
}
if (cardState == 0 && !NO_TURN)
{
GetComponent<Image>().sprite = back; // shows card back
}
else if (cardState == 1 && !NO_TURN)
{
GetComponent<Image>().sprite = face; // shows card front
}
}
//setters and getters
public int Number
{
get {return cardNumber;}
set { cardNumber = value;}
}
public int State
{
get { return cardState; }
set { cardState = value; }
}
public bool SetUp
{
get { return _setUp; }
set { _setUp = value; }
}
public void PairCheck()
{
StartCoroutine(pause ());
}
IEnumerator pause()
{
yield return new WaitForSeconds(1);
if (cardState == 0)
{
GetComponent<Image>().sprite = back;
}
else if (cardState == 1)
{
GetComponent<Image>().sprite = face;
}
}
}
Thank you for reading, I will post a link to the github repository if that helps.
github repository
Your deck array has at least one card in it that has _setUp set to true which would make it go in a infinite loop.
The reason it goes in a infinite loop is because it will have set all available _setUp to true and it would keep looking for _setUp that are set to false and it will never find any.
The reason you need at least 26 object that have _setUp to false is because in the nested for loop you loop 13 times and then you do that twice which gives a total of 26 loops. So you need at least 26 objects.
What you can do to make sure that they're all false is to set them all to false before entering the for loop
for(int i = 0; i < deck.Length; i++)
{
deck[i].GetComponent<Card>().SetUp = false;
}
public class green : MonoBehaviour
{
private AudioSource source;
public AudioClip sound;
static int result = 0;
void Start()
{
StartCoroutine("RoutineCheckInputAfter3Minutes");
Debug.Log("a");
}
IEnumerator RoutineCheckInputAfter3Minutes()
{
System.Random ran = new System.Random();
int timeToWait = ran.Next(1, 50) * 1000;
Thread.Sleep(timeToWait);
source = this.gameObject.AddComponent<AudioSource>();
source.clip = sound;
source.loop = true;
source.Play();
System.Random r = new System.Random();
result = r.Next(1, 4);
Debug.Log("d");
yield return new WaitForSeconds(3f * 60f);
gm.life -= 1;
Debug.Log("z");
}
public void Update()
{
if (result == 1 && gm.checka == true)
{
Debug.Log("e");
StopCoroutine("RoutineCheckInputAfter3Minutes");
gm.life += 1;
source.Stop();
gm.checka = false;
Debug.Log("j");
}
if (result == 2 && gm.checkb == true)
{
Debug.Log("f");
StopCoroutine("RoutineCheckInputAfter3Minutes");
gm.life += 1;
source.Stop();
gm.checkb = false;
Debug.Log("z");
}
else if (result == 3 && gm.checkc == true)
{
StopCoroutine("RoutineCheckInputAfter3Minutes");
Debug.Log("g");
gm.life += 1;
source.Stop();
gm.checkc = false;
Debug.Log(gm.life);
}
}
}
There are two problems
I want to make music stop and life variable decrease -1, if the user doesn't push any buttons for 3 minutes. But if the user pushes the right button, life variable will be increased + 1. but I don't know how to get null input from the user for 3 minutes.
If I use while for this program, this shuts down… until life is below 0, I want to repeat music which is played on irregular time.
Edit: Don't use Thread in Unity
How Unity works
Alternative to Thread
Couroutine Example 1
Coroutine Example 2
* To put it simple, Thread.Sleep hangs to Unity and Unity can't operate for the time, and that's why it looks like running slow.
You can use coroutines for this problem.
void Start()
{
StartCoroutine("RoutineCheckInputAfter3Minutes");
}
IEnumerator RoutineCheckInputAfter3Minutes()
{
yield return new WaitForSeconds(3f*60f);
gm.life -= 1;
}
void RightButtonClicked()
{
gm.life += 1;
StopCoroutine("RoutineCheckInputAfter3Minutes");
StartCoroutine("RoutineCheckInputAfter3Minutes");
}
Or, you can just turn your a function into a coroutine, if the other code sections work.
void Start()
{
StartCoroutine(a());
}
public IEnumerator a()
{
while (gm.life >= 0)
{
System.Random ran = new System.Random();
int timeToWait = ran.Next(1, 50) * 1000;
yield return new WaitForSeconds(timeToWait);
source = this.gameObject.AddComponent<AudioSource>();
source.clip = sound;
source.loop = true;
source.Play();
System.Random r = new System.Random();
result = r.Next(1, 4);
Debug.Log("d");
}
}
This is an example usage of coroutine based on your code More than this is out of scope:
A pastebin link to code