How do I wait until a bool is true in unity? - c#

I am trying to make it so that when a button is pressed, the text would update.
My code:
public Text TextField;
public bool narrationGoing = true;
public void SetText(string text)
{
TextField.text = text;
}
public void ResumeNarration()
{
narrationGoing=true;
}
IEnumerator WaitNarration()
{
narrationGoing = false;
Debug.Log(narrationGoing);
yield return new WaitWhile(() => narrationGoing == false);
}
void Start()
{
Narration();
}
void Narration()
{
SetText("HeLlO wOrLd");
StartCoroutine(WaitNarration());
SetText("The button worked!");
}
However, when I launch the game it triggers the "WaitNarration()" coroutine, because it does write "false", but then it skips over the yield completely. I've tried to find a solution but sadly nothing worked, does anyone have an answer for how I can fix this? Thank you in advance!
Also, ResumeNarration() is a function for the button, that's why it's not triggered in the code.

You can yield inside of a while loop until the condition is met.
IEnumerator WaitNarration()
{
Debug.Log("WaitNarration started");
while (!narrationGoing)
{
yield return null;
}
Debug.Log("WaitNarration complete");
}

Related

Unity - Advertisment called multiple times

I'm having this problem using Unity's Advertisment. Specifically after watching the video and clicking the X button to close the video, I should give the prize to the player (go to the next level). The problem is that the OnUnityAdsDidFinish function calls if (showResult == ShowResult.Finished) multiple times. What am I doing wrong? how do I call the FindObjectOfType () .LoadNextLevel () function once; ? Thank you in advance
public class UnityAdsInterstitial : MonoBehaviour, IUnityAdsListener
{
private string gameID = "******";
//nome scelto nella DashBoard di Unity
private string interstitialID = "interstitial";
private string myPlacementId = "rewardedVideo";
public int randomHighValue = 30;
private bool TestMode = true;
private bool adsClosed = false;
public Button _button;
private void Awake()
{
_button = GetComponent<Button>();
}
void Start()
{
Debug.Log("Ads start");
_button = GameObject.Find("StartAds").GetComponent<Button>();
_button.interactable = Advertisement.IsReady(myPlacementId);
if (_button) _button.onClick.AddListener(ShowRewardedVideo);
Advertisement.Initialize(gameID, TestMode);
Advertisement.AddListener(this);
if (adsClosed)
{
adsClosed = false;
}
}
public void ShowInterstitial()
{
if (Advertisement.IsReady(interstitialID) )
{
Advertisement.Show(interstitialID);
}
}
public void ShowRewardedVideo()
{
if (Advertisement.IsReady(myPlacementId))
{
Debug.Log("Rewarded video is Ready");
Advertisement.Show(myPlacementId);
}
else
{
Debug.Log("Rewarded video is not ready at the moment! Please try again later!");
}
}
public void HideBanner()
{
Advertisement.Banner.Hide();
}
public void OnUnityAdsReady(string placementdID)
{
if (placementdID == interstitialID)
{
Debug.Log("InterstitialIsReady");
}
if (placementdID == myPlacementId)
{
Debug.Log("RewardedIsReady");
_button.interactable = true;
}
}
public void OnUnityAdsDidFinish(string placementdID, ShowResult showResult)
{
if (showResult == ShowResult.Finished)
{
// Reward the user for watching the ad to completion.
if (!adsClosed)
{
adsClosed = true;
FindObjectOfType<LevelLoader>().LoadNextLevel();
}
}
else if (showResult == ShowResult.Skipped)
{
// Do not reward the user for skipping the ad.
}
else if (showResult == ShowResult.Failed)
{
Debug.LogWarning("The ad did not finish due to an error.");
}
}
public void OnUnityAdsDidError(string message)
{
Debug.Log("OnUnityAdsDidError");
}
public void OnUnityAdsDidStart(string message)
{
Debug.Log("OnUnityAdsDidStart");
}
I would start my investigation by checking the placement id (in case there are more placements)
Checking that the callback is for the proper placement id
if (showResult == ShowResult.Finished && placementId == myPlacementId)
{
// Reward the user for watching the ad to completion.
if (!adsClosed)
{
adsClosed = true;
FindObjectOfType<LevelLoader>().LoadNextLevel();
}
}
The second would be that I only have one active instance of UnityAdsInterstitial. You can check this in debug mode by the reference of the object. If more than one instance starts from somewhere else in your code, then you should just limit to one.

C# Unity Queue emptying itself

I have trouble figuring out a bug. My game initializes several queues to store the game prompts and, in the "Start" button, the function StartDialogue() fills each of the queues and goes to DisplayNext(). The DisplayNext() function is supposed to dequeue the first prompt from certain queues and put them into the dialogue boxes. Afterwards, the player selects the correct button to answer and it calls DisplayNext() to advance to the next prompt.
However, my problem is that my sen_queue count is setting itself to 0 when the correct button calls DisplayNext(). The debug output first displays 4 when "Start" is pressed, but when the correct button is pressed, it displays 0. With the first correct button press, it goes to the EndDialogue() function. I have attached my code below. I have no clue how to fix this so any help would be greatly appreciated! Thank you so much in advance!
However, my problem is that my sen_queue count is setting itself to 0 when the correct button calls DisplayNext(). The debug output first displays 4 when "Start" is pressed, but when the correct button is pressed it displays 0. With the first correct button press it goes to the EndDialogue() function. I have attached my code below. I have no clue how to fix this, so any help would be greatly appreciated! Thank you so much in advance!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class QuestionManager : MonoBehaviour
{
public Text dialogueText;
public Text topText;
public Text botText;
public Queue<string> sen_queue;
public Queue<string> top_queue;
public Queue<string> bot_queue;
public Queue<string> wrong_queue;
private bool prev_wrong;
public GameObject finalsaltwater;
public GameObject water;
public GameObject sand;
public GameObject salt;
public GameObject filtersand;
public GameObject platewater;
void Start()
{
Screen.SetResolution(1600, 900, true);
sen_queue = new Queue<string>();
top_queue = new Queue<string>();
bot_queue = new Queue<string>();
wrong_queue = new Queue<string>();
prev_wrong = false;
finalsaltwater.SetActive(false);
filtersand.SetActive(false);
platewater.SetActive(false);
}
public void StartDialogue(Question question, Question topprompt, Question botprompt, Question wrong)
{
sen_queue.Clear();
top_queue.Clear();
bot_queue.Clear();
wrong_queue.Clear();
foreach (string sentence in question.sentences)
{
sen_queue.Enqueue(sentence);
}
foreach (string sentence in topprompt.sentences)
{
top_queue.Enqueue(sentence);
}
foreach (string sentence in botprompt.sentences)
{
bot_queue.Enqueue(sentence);
}
foreach (string sentence in wrong.sentences)
{
wrong_queue.Enqueue(sentence);
}
DisplayNext();
}
IEnumerator ToggleFalse(GameObject thing)
{
yield return new WaitForSeconds(1.5f);
thing.SetActive(false);
}
IEnumerator ToggleTrue(GameObject thing)
{
yield return new WaitForSeconds(1.5f);
thing.SetActive(true);
}
public void DisplayNext()
{
Debug.Log("IN DISPLAY " + sen_queue.Count);
if (sen_queue.Count == 0)
{
EndDialogue();
return;
}
if (top_queue.Count == 2)
{
platewater.SetActive(true);
water.SetActive(false);
StartCoroutine(ToggleFalse(salt));
} else if (top_queue.Count == 1)
{
filtersand.SetActive(true);
sand.SetActive(false);
platewater.SetActive(false);
StartCoroutine(ToggleTrue(finalsaltwater));
} else if (top_queue.Count == 0)
{
platewater.SetActive(true);
StartCoroutine(ToggleTrue(salt));
StartCoroutine(ToggleFalse(platewater));
finalsaltwater.SetActive(false);
}
string sentence = sen_queue.Dequeue();
if (top_queue.Count != 0)
{
string topbutton = top_queue.Dequeue();
topText.text = topbutton;
}
if (bot_queue.Count != 0)
{
string botbutton = bot_queue.Dequeue();
botText.text = botbutton;
}
prev_wrong = false;
dialogueText.text = sentence;
}
public void IncorrectDisplay()
{
if (wrong_queue.Count == 0)
{
return;
}
if (prev_wrong == false)
{
string wrong_sen = wrong_queue.Dequeue();
dialogueText.text = wrong_sen;
}
prev_wrong = true;
}
public void EndDialogue()
{
dialogueText.text = "Good job! Refresh the page if you want to restart.";
return;
}
}
I'm somewhat late, but I had the same issue. And even if you've already solved the problem, maybe I can help some other people in the future!
It has something to do with the calling order. In my code I had 2 Start() methods in 2 different classes.
In the class DialogueTrigger:
public Dialogue dialogue;
public void Start()
{
triggerDialogue();
}
public void triggerDialogue()
{
FindObjectOfType<DialogueManager>().startDialogue(dialogue);
}
And in the class DialogueManager:
public Queue<string> sentences;
private bool dialogueStarted;
// Start is called before the first frame update
void Start()
{
sentences = new Queue<string>();
}
private void Update()
{
if(dialogueStarted && Input.GetKeyDown(GameObject.Find("Player").GetComponent<Controls>().confirm))
{
displayNextSentence();
}
}
public void startDialogue(Dialogue dialogue)
{
print("start:" + dialogue.name);
sentences.Clear();
foreach (string s in dialogue.sentences)
{
sentences.Enqueue(s);
}
displayNextSentence();
dialogueStarted = true;
}
public void displayNextSentence()
{
if (sentences.Count == 0)
{
endDialogue();
return;
}
string sentence = sentences.Dequeue();
Debug.Log(sentence);
}
private void endDialogue()
{
Debug.Log("End of dialogue");
dialogueStarted = false;
}
Instead of clicking a button I let the next string come in when pressing a key (spacebar), but it should work the same.
The problem was that the Start() of DialogueManager got called after the startDialogue()-method (which gets called in the Start() of DialogueTrigger), gets called second. This resets the whole queue, makes it empty and thus sets the length to 0. This means the press of the key doesn't reset the queue, but it happens automatically!
In your code for some reason the Start() method gets called after the button press.
The solution to is to put the following code at the top of your StartDialogue()-method:
if (sentences == null)
{
sentences = new Queue<string>();
}
You could probably also leave the if-statement out and then use this code instead of the Clear()-methods. I haven't tested this, but the result should be the same. If you want to use Clear(), it would be better to use the if-statement. If you still leave it in you would reset the queue twice, which would be unnecessary.

Thread seems to stop running when I call a given method in the same class, why?

I have a class that constantly refreshes devices physically connected to PC via USB. The monitoring method runs on a thread checking a _monitoring flag, and Start and Stop methods just set and unset that flag.
My current problem is: when the thread is running, I get the expected "busy" and "not busy" console prints, but when I call Stop method, it keeps running while(_busy) forever, because somehow the _monitoringThread seems to stop running!
I suspect it stops running because the last print is always busy, that is, the ExecuteMonitoring runs midway and then nobody knows (at least I don't).
Pause debugging and looking at StackTrace didn't help either, because it keeps in the while(_busy) statement inside Stop() method, forever.
public class DeviceMonitor
{
bool _running;
bool _monitoring;
bool _busy = false;
MonitoringMode _monitoringMode;
Thread _monitoringThread;
readonly object _lockObj = new object();
// CONSTRUTOR
public DeviceMonitor()
{
_monitoringThread = new Thread(new ThreadStart(ExecuteMonitoring));
_monitoringThread.IsBackground = true;
_running = true;
_monitoringThread.Start();
}
public void Start()
{
_monitoring = true;
}
public void Stop()
{
_monitoring = false;
while (_busy)
{
Thread.Sleep(5);
}
}
void ExecuteMonitoring()
{
while (_running)
{
Console.WriteLine("ExecuteMonitoring()");
if (_monitoring)
{
lock (_lockObj)
{
_busy = true;
}
Console.WriteLine("busy");
if (_monitoringMode == MonitoringMode.SearchDevices)
{
SearchDevices();
}
else
if (_monitoringMode == MonitoringMode.MonitorDeviceConnection)
{
MonitorDeviceConnection();
}
lock (_lockObj)
{
_busy = false;
}
Console.WriteLine("not busy");
}
Thread.Sleep(1000);
_busy = false;
}
}
private void SearchDevices()
{
var connected = ListDevices();
if (connected.Count > 0)
{
Device = connected.First();
ToggleMonitoringMode();
}
else
Device = null;
}
void MonitorDeviceConnection()
{
if (Device == null)
{
ToggleMonitoringMode();
}
else
{
bool responding = Device.isConnected;
Console.WriteLine("responding " + responding);
if (!responding)
{
Device = null;
ToggleMonitoringMode();
}
}
}
void ToggleMonitoringMode()
{
if (_monitoringMode == MonitoringMode.SearchDevices)
_monitoringMode = MonitoringMode.MonitorDeviceConnection;
else
if (_monitoringMode == MonitoringMode.MonitorDeviceConnection)
_monitoringMode = MonitoringMode.SearchDevices;
}
enum MonitoringMode
{
SearchDevices,
MonitorDeviceConnection
}
}
The most likely explanation is: optimization: The compiler sees that _busy is never changed inside the Stop method and it is therefore allowed to convert this to an endless loop by replacing _busy with true. This is valid, because the _busy field is not marked as being volatile and as such the optimizer doesn't have to assume changes happening on another thread.
So, try marking _busy as volatile. Or, even better - actually A LOT BETTER - use a ManualResetEvent:
ManualResetEvent _stopMonitoring = new ManualResetEvent(false);
ManualResetEvent _monitoringStopped = new ManualResetEvent(false);
ManualResetEvent _stopRunning = new ManualResetEvent(false);
public void Stop()
{
_stopMonitoring.Set();
_monitoringStopped.Wait();
}
void ExecuteMonitoring()
{
while (!_stopRunning.Wait(0))
{
Console.WriteLine("ExecuteMonitoring()");
if(!_stopMonitoring.Wait(0))
{
_monitoringStopped.Unset();
// ...
}
_monitoringStopped.Set();
Thread.Sleep(1000);
}
}
Code is from memory, might contain some typos.

NullReferenceException on Public Method Unity3D

void Update () {
if (isSaveNeeded){
AutoSaveData();
isSaveNeeded = false;
}
string status;
if (Social.localUser.authenticated) {
status = "Authenticated ";
if (isLoaded == false){
LoadAutoSave();
isLoaded = true;
}
}
else {
status = "Not Authenticated";
}
statusToPass = status + " " + mMsg;
}
public void OnSignIn() {
if (Social.localUser.authenticated) {
PlayGamesPlatform.Instance.SignOut();
}
else {
PlayGamesPlatform.Instance.Authenticate(mAuthCallback, false);
}
}
public void LoadData() {
((PlayGamesPlatform)Social.Active).SavedGame.ShowSelectSavedGameUI("Select Saved Game to Load",
10,
false,
true,
SaveGameSelectedForRead);
}
public void SaveData() {
((PlayGamesPlatform)Social.Active).SavedGame.ShowSelectSavedGameUI("Save Game Progress",
10,
true,
false,
SaveGameSelectedForWrite);
}
public void AutoSaveData() {
((PlayGamesPlatform)Social.Active).SavedGame.OpenWithAutomaticConflictResolution(autoSaveFileName,
DataSource.ReadCacheOrNetwork,
ConflictResolutionStrategy.UseLongestPlaytime,
SavedGameOpenedForWrite);
}
public void LoadAutoSave() {
((PlayGamesPlatform)Social.Active).SavedGame.OpenWithAutomaticConflictResolution(autoSaveFileName,
DataSource.ReadCacheOrNetwork,
ConflictResolutionStrategy.UseLongestPlaytime,
SavedGameOpenedForRead);
}
I am trying to call a public method in the same script called AutoSaveData()
and it is giving me Null reference exception.
I have also added DontDestroyOnLoad to this script so that the game object persists between scenes.
I have been looking into it for some hours now and couldn't figure out the cause for it. It might be a simple mistake on my part but as I am new to coding, probably I am not able to figure it out.
Thanks
I fixed it by adding condition to my AutoSaveData() function.
Now it is like
if(Social.localuser.authenticated){ AutoSaveData(){
} }
Thanks

Facebook.init() function still causing null exception

Background:
I am trying to integrate facebook for a unity-android project and I can't seem to make it working, I have looked on the fb page and allot of other place but can't seem to find a what I am doing wrong.
Problem:
When trying FB.Login i get the reference exception: Facebook object is not loaded. Did you call FB.init?
Code for InitializeFB.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Facebook.MiniJSON;
using System;
public class ConncetToFaceBook : MonoBehaviour {
// Connect to facebook
void Awake () {
// Required
DontDestroyOnLoad(gameObject);
// Initialize FB SDK
enabled = false;
FB.Init(onInitComplete, OnHideUnity);
//Display id
Debug.Log (FB.UserId);
//Login to facebook
FB.Login("email,publish_actions", LoginCallback);
}
/* Helper Methods */
private void onInitComplete ()
{
enabled = true; // "enabled" is a property inherited from MonoBehaviour
if (FB.IsLoggedIn)
{
//Some Code
}
}
private void OnHideUnity(bool isGameShown)
{
//some code
}
void LoginCallback(FBResult result)
{
if (FB.IsLoggedIn)
{
OnLoggedIn();
}
}
void OnLoggedIn()
{
Debug.Log("Logged in. ID: " + FB.UserId);
}
}
Code for FB.cs.init()
public static void Init(
InitDelegate onInitComplete,
string appId = "{My app ID}", //I did put my own here. Plus I use " instead of ' because ' give me a error.
bool cookie = true,
bool logging = true,
bool status = true,
bool xfbml = true,
bool frictionlessRequests = true,
HideUnityDelegate onHideUnity = null,
string authResponse = null)
FB.Init() is asynchronous method - it doesn't make the program wait until it is finished. And your FB.Login() is called too soon, you need to call it after FB.Init() is ready - inside the onInitComplete() method.
My setup:
void FBConnect(){
if(!FB.IsInitialized){
Debug.Log("Initializing FB");
FB.Init(FBInitCallback, null,null);
} else {
Debug.Log("No need for FB init");
FBInitCallback();
}
}
private void FBInitCallback(){
Debug.Log("FB init OK");
if(!FB.IsLoggedIn){
FB.Login("email,user_friends", FBLoginCallback);
} else {
//GetHisFBDataNow();
Debug.Log("Everything is known about this guy");
}
}
private void FBLoginCallback(FBResult result){
if (result.Error != null){
Debug.Log("FB Error Response:\n" + result.Error);
} else if (!FB.IsLoggedIn) {
Debug.Log("FB Login cancelled by Player");
} else {
//GetHisFBDataNow();
Debug.Log("Now also everything is known about this guy");
}
}
Hey idk if that will help but can you try and put fb.log in init complete?

Categories