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.
Related
The Idea is, that my Data get loaded, asynchron, when my app startet. Because the user normaly need some seconds to interact and in this time, i should get my data. (For understanding reasons, it will be a book app, with some books)
To do this, in loadingprofile i open a method on a static class to start loading.
In the Screen where i want to show my data, i check with a method on the static class, if the data are there ( and wait if theire not).
So in the Profil loading screen i did this:
public class LoadProfiles : MonoBehaviour
{
private void Awake()
{
if (!LoadingData.dataLoaded)
{
LoadingData.LoadData();
}
}
void Start()
{
//Loading Profile Dataa
}
}
Then when the User click on a profile, i open the next scene "Bookshelf" and a game object in it has the following script attachted to read the data (the book categories). so i open the wait on data method, that shoud return, when the data are arrived.
public class PopulateShelf : MonoBehaviour
{
private void Start()
{
LoadingData.WaitOnData();
LoadingData.Categories.ForEach(c => {
//create categories
});
}
}
The static loading class looks like the following
public class LoadingData : MonoBehaviour
{
public static List<CategoryDTO> Categories;
public static IEnumerator CategoriesRequest;
public static bool dataLoaded = false;
public static void LoadData()
{
CategoriesRequest = LoadCategories();
dataLoaded = true;
}
public static void WaitOnData()
{
if (!dataLoaded) LoadData();
while (CategoriesRequest.MoveNext())
{
//Log wait on categories
}
}
public static IEnumerator LoadCategories()
{
UnityWebRequest request = UnityWebRequest.Get(Constants.apiURL + Constants.apiURLCategories);
yield return request.SendWebRequest();
while (!request.isDone)
{
yield return true;
}
if (request.result == UnityWebRequest.Result.ConnectionError)
{
//Log error
}
else
{
Categories = JsonConvert.DeserializeObject<List<CategoryDTO>>(request.downloadHandler.text);
Categories.Sort((x, y) => x.Order.CompareTo(y.Order));
}
}
}
So, the problem is that the on the loging screen, it opens the LoadingData.LoadData Method (but it do not start loading).
When im in the Bookshelf Scene, and open LoadingData.WaitOnData it starts loading the data and the User have to wait.
I also tried it with StartCoroutines. I refactored the LoadingData class, to a non static class. Startet the Coroutings in the LoadData Method.
First it looked like it worked, the data was loaded in the profileScene
Because of it was not static, i had to pass somehow the Object. To do this i created in a static script a Constants for a static LoadingData script variable.
In the Profile Scene i attached it and loaded the data. When i wanted to read it in the BookShefScene, i got a null refrence in my static class...
Here would be the class with coroutines
public class LoadingData : MonoBehaviour
{
public Dictionary<int, BookDTO> Books = new Dictionary<int, BookDTO>();
public List<CategoryDTO> Categories;
public bool dataLoaded = false;
public void LoadData()
{
StartCoroutine(LoadCategories());
StartCoroutine(LoadBooks());
dataLoaded = true;
}
public void WaitOnData()
{
if (!dataLoaded) LoadData();
while (categorieLoaded)
{
//log waiting
}
}
public IEnumerator LoadCategories()
{
UnityWebRequest request = UnityWebRequest.Get(Constants.apiURL + Constants.apiURLCategories);
yield return request.SendWebRequest();
while (!request.isDone)
{
yield return true;
}
if (request.result == UnityWebRequest.Result.ConnectionError)
{
//Log Error
}
else
{
Categories = JsonConvert.DeserializeObject<List<CategoryDTO>>(request.downloadHandler.text);
Categories.Sort((x, y) => x.Order.CompareTo(y.Order));
}
}
}
in profile loading screen, to attache my script to the static variable, i said something like this:
Constants.loadingData = this.GetComponent<LoadingData>();
Constants.loadingData.LoadData();
and in the bookshelf i said
Constants.loadingData.WaitOnData();
Constants.loadingData.Categories();
I checked with the Debuger, in the Profile Scene the Constants.loadingData was created and referenced.
In the Bookshelf Scene, i dont saw the referenced object (it was null)...
can anyoune help me with one of this solution? or have a third solution?
I think your condition should be
if(!dataLoaded) LoadData();
Same for the categorieLoaded, maybe the name is misleading and you are using them correctly (?)
Is the GameObject stored in Constants.loadedData set as dontDestroyOnLoad? Maybe when you change scene it gets destroied and set to null.
Also I would suggest to have a better way of waiting for the data to load, if it takes seconds the user will not be able to interract with the game since the while loop will freeze the entire window, you can make your own method that checks every frame if the data got loaded.
private IEnumerable WaitForData()
{
while(!Constants.loadedData.Loaded) return null;
// populate the bookshelf
// foreach(Book book in Constants.Books)
}
You need to call LoadData() somewhere before calling this, you could setup a loading screen while waiting aswell to make players understand.
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");
}
I have a problem here with Photon's PUN 2. Sometimes It works, but other times it doesn't. Since the last 2 weeks it isn't working that fine. Before it were better, I joined to the master, and then, to the lobby and It allowed me to list the rooms and join them without any problem. Now, I haven't changed that code that much, I only changed it after the errors started. Now, sometimes it joins a match, but another it doesn't, showing the following error:
JoinRandomRoom failed. Client is not on Master Server or not yet ready to call operations. Wait for callback: OnJoinedLobby or OnConnectedToMaster.
I only have 2 devices to test my online with PUN, but, even if I created a room is not working anymore, it just seems like works randomly. Here's my code if you want to check it:
void Start()
{
PhotonNetwork.ConnectUsingSettings();
}
// Update is called once per frame
void Update()
{
}
public override void OnJoinedLobby()
{
// /* print("Connected to lobby")*/;
}
public override void OnConnectedToMaster()
{
//This shows a popup to let know the player that is connected
//PhotonNetwork.JoinLobby();
base.OnJoinedLobby();
GameObject.Find("IWifi").SetActive(false);
print("Puga");
StatePScript.IsShow = true;
Connected = true;
GameObject.Find("IOk").GetComponent<Image>().color = Color.white;
print("IOKS Value is " + IOKS.Show);
GameObject.Find("StatusText").GetComponent<Text>().text = "Connected!";
GameObject.Find("StatusText").GetComponent<Text>().color = Color.green;
A.Play();
GameObject.Find("StatePanel").GetComponent<Animator>().SetBool("Show", false);
IOKS.Show = false;
GameObject.Find("IOk").GetComponent<Image>().color = new Color(0, 0, 0, 0);
Connected = false;
StatePScript.IsShow = false;
}
I have other photon scripts like this that is the random room code:
using UnityEngine.UI;
using Photon.Pun;
public class RandomBScript : MonoBehaviourPunCallbacks
{
// Use this for initialization
private Button B;
void Start ()
{
B = GetComponent<Button>();
B.onClick.AddListener(Clicker);
}
void Clicker()
{
PhotonNetwork.JoinRandomRoom();
print("Random");
}
public override void OnJoinRandomFailed(short returnCode, string message)
{
base.OnJoinRandomFailed(returnCode, message);
print(message);
}
}
The create room code:
using Photon.Pun;
using Photon.Realtime;
public class CreateRoomS : MonoBehaviourPunCallbacks
{
// Use this for initialization
private Button B;
private InputField IF;
private InputField PlayerField;
public AudioSource A;
void Start ()
{
B = GetComponent<Button>();
PlayerField = GameObject.Find("PlayerInput").GetComponent<InputField>();
IF = GameObject.Find("NameInput").GetComponent<InputField>();
B.onClick.AddListener(Clicker);
}
private void Awake()
{
}
void Clicker()
{
print("Trying To create a room...");
if (IF.text.Length > 0 && IF.text.Length <= 20)
{
int PlayerAmount = Int32.Parse(PlayerField.text);
RoomOptions roompos = new RoomOptions()
{
IsVisible = true, IsOpen = true, MaxPlayers = (byte)PlayerAmount
};
PhotonNetwork.CreateRoom(IF.text, roompos);
print("RoomCreated!");
}
else
{
A.Play();
}
}
public override void OnJoinedRoom()
{
print("We are in a room");
PhotonNetwork.LoadLevel("WaitScene");
}
public override void OnCreateRoomFailed(short returnCode, string message)
{
base.OnCreateRoomFailed(returnCode, message);
}
public override void OnCreatedRoom()
{
base.OnCreatedRoom();
print("We created the room");
}
// Update is called once per frame
public override void OnConnectedToMaster()
{
base.OnConnectedToMaster();
print("Connected to the master");
}
And finally, the list rooms code:
using Photon.Pun;
using Photon.Realtime;
using System.Reflection;
using System;
public class RooManager : MonoBehaviourPunCallbacks
{
// Use this for initialization
public GameObject roomPrefab;
public Sprite Four, Two, Three;
private string RoomName;
private int PlayerAmount;
private int MaxPlayers;
private Image I;
private Vector2 RoomVector;
private bool Lock = false;
public GameObject Content;
private List<RoomInfo> RoomList;
private bool IsntNull = false;
private Dictionary<string, RoomInfo> cachedRoomList;
private Dictionary<string, GameObject> roomListEntries;
private void Awake()
{
GameObject.Find("StatePanel").GetComponent<Animator>().SetBool("Show", true);
cachedRoomList = new Dictionary<string, RoomInfo>();
roomListEntries = new Dictionary<string, GameObject>();
}
void Start ()
{
Content = GameObject.Find("Content").GetComponent<GameObject>();
RoomVector = new Vector2(370, this.transform.position.y);
}
void Rooming()
{
PhotonNetwork.JoinLobby();
}
private void ClearRoomListView()
{
foreach (GameObject entry in roomListEntries.Values)
{
Destroy(entry.gameObject);
}
roomListEntries.Clear();
}
private void UpdateRoomListView()
{
foreach (RoomInfo Item in cachedRoomList.Values)
{
RoomName = Item.Name;
PlayerAmount = Item.PlayerCount;
MaxPlayers = Item.MaxPlayers;
RoomVector.y -= 100;
GameObject RoomPrefab = Instantiate(roomPrefab, RoomVector, transform.rotation) as GameObject;
if (Item.PlayerCount == 0)
{
Destroy(RoomPrefab);
}
print(PhotonNetwork.CurrentLobby.Name);
RoomPrefab.transform.Find("RoomName").GetComponent<Text>().text = RoomName;
RoomPrefab.transform.Find("PlayerInt").GetComponent<Text>().text = PlayerAmount.ToString();
if (Item.PlayerCount == 0)
{
}
if (Item.MaxPlayers == 4)
{
GameObject.Find("IPlayerA").GetComponent<Image>().sprite = Four;
}
else if (Item.MaxPlayers == 2)
{
GameObject.Find("IPlayerA").GetComponent<Image>().sprite = Two;
}
else if (Item.MaxPlayers == 3)
{
GameObject.Find("IPlayerA").GetComponent<Image>().sprite = Three;
}
RoomPrefab.transform.SetParent(Content.transform);
}
}
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
ClearRoomListView();
UpdateCachedRoomList(roomList);
UpdateRoomListView();
print("Updated");
}
private void UpdateCachedRoomList(List<RoomInfo> roomList)
{
foreach (RoomInfo info in roomList)
{
// Remove room from cached room list if it got closed, became invisible or was marked as removed
if (!info.IsOpen || !info.IsVisible || info.RemovedFromList)
{
if (cachedRoomList.ContainsKey(info.Name))
{
cachedRoomList.Remove(info.Name);
}
continue;
}
// Update cached room info
if (cachedRoomList.ContainsKey(info.Name))
{
cachedRoomList[info.Name] = info;
}
// Add new room info to cache
else
{
cachedRoomList.Add(info.Name, info);
}
}
}
This script instantiates a button for every room in photon's server. If you click one button, you join that room. As I said, sometimes it work and sometimes it doesn't, sometimes it helped to comment the line PhotonNetwork.JoinLobby(), but that means that you wont see the romos. Actually, even with or without the JoinLobby() line, it isn't working that well.
I want to create a dialog box, so I used the script dialog.cs. I want to call this script, for example, I want to click the object and make appear the windows dialog. Unfortunatly, when I click the object the windows dialog doens't appear.
Ps: Since the dialog script is a MonoBehaviour, I even added it to a object to display it.
gameObject.AddComponent<Dialog>();
Full dialog.cs
using UnityEngine;
using System.Collections.Generic;
public class Dialog : MonoBehaviour {
public TextAsset conversationAsset;
protected string dialogname="";
public string description="";
public bool keyTriggered=true;
public bool restartDialogEachTime=true;
public string triggerInputKey="Chat";
public string dialogClass="DialogGuiBasic";
public float dialogRange=3;
public GUISkin dialogSkin;
public Camera dialogCamera=null;
public Vector2 dialogSize=new Vector2(800,371);
public Texture2D dialogPortrait=null;
public float charactersPerSecond=50f;
private Conversation[] conversations=null;
private int conversationIndex=0;
private GameObject playerObject;
public void Start(){
LoadDialog();
playerObject=GameObject.FindWithTag("Player");
// Add this to the parley list
Parley.GetInstance().GetDialogs().Add(dialogname,this);
}
public int GetConversationIndex(){
return conversationIndex;
}
public void SetConversationIndex(int conversationIndex){
this.conversationIndex=conversationIndex;
}
public Conversation[] GetConversations(){
return conversations;
}
protected void LoadDialog(){
dialogname=conversationAsset.name.Substring(4);
Debug.Log("name=["+dialogname+"]");
// Load from Text Asset
string[] lines = conversationAsset.text.Split("\n"[0]);
int l=0;
conversations=new Conversation[int.Parse(lines[l++])];
for (int c=0;c<conversations.Length;c++){
l++;
Conversation co=new Conversation();
conversations[c]=co;
co.id=int.Parse(lines[l++]);
co.returnId=int.Parse(lines[l++]);
co.returnDialogName=lines[l++];
co.text=null;
for (string text=lines[l++];!text.Equals(Conversation.BREAK);text=lines[l++]){
if (co.text!=null){
co.text+="\n";
}
co.text+=text;
}
co.repeattext=null;
for (string text=lines[l++];!text.Equals(Conversation.BREAK);text=lines[l++]){
if (co.repeattext!=null){
co.repeattext+="\n";
}
co.repeattext+=text;
}
co.once=bool.Parse(lines[l++]);
co.fallthrough=bool.Parse(lines[l++]);
co.questevent=lines[l++];
co.playerCommands=new Command[int.Parse(lines[l++])];
for (int ct=0;ct<co.playerCommands.Length;ct++){
Command command=new Command();
co.playerCommands[ct]=command;
command.objectName=lines[l++];
command.method=lines[l++];
command.assignment=("true".Equals(lines[l++]));
command.paramaters=new string[int.Parse(lines[l++])];
for (int pt=0;pt<command.paramaters.Length;pt++){
command.paramaters[pt]=lines[l++];
}
}
co.questrequirement=lines[l++];
co.environmentalrequirement=lines[l++];
co._available=bool.Parse(lines[l++]);
if (co.repeattext==null || co.repeattext.Length==0) co.repeattext=null;
if (co.questevent==null || co.questevent.Length==0) co.questevent=null;
if (co.playerCommands==null || co.playerCommands.Length==0) co.playerCommands=null;
if (co.questrequirement==null || co.questrequirement.Length==0) co.questrequirement=null;
co.options=new Option[int.Parse(lines[l++])];
for (int n=0;n<co.options.Length;n++){
l++;
Option o=new Option();
co.options[n]=o;
o.text=lines[l++];
for (string text=lines[l++];!text.Equals(Conversation.BREAK);text=lines[l++]){
if (o.text!=null){
o.text+="\n";
}
o.text+=text;
}
o.destinationId=int.Parse(lines[l++]);
o.destinationDialogName=lines[l++];
o._available=bool.Parse(lines[l++]);
l++;
}
l++;
}
}
public void Update(){
if (triggerInputKey!=null && triggerInputKey.Length>0 && keyTriggered && Input.GetButtonUp(triggerInputKey) && IsInRange() && !Parley.GetInstance().IsInGui()){
TriggerDialog();
}
}
public bool IsInRange(){
return Vector3.Distance(transform.position, playerObject.transform.position)<dialogRange;
}
public void TriggerDialog(){
TriggerDialog(restartDialogEachTime?0:conversationIndex);
}
public void TriggerDialog(int start){
conversationIndex=start;
if (dialogClass!=null && dialogClass.Trim().Length>0){
// Make sure we only ever add one gui instance
MonoBehaviour dialogGuiInstance=(MonoBehaviour)GetComponent(dialogClass);
if (dialogGuiInstance==null){
// Add dialog gui now
gameObject.AddComponent<Dialog>();
}
}
SendMessage("StartDialog",this,SendMessageOptions.RequireReceiver);
}
public void TriggerDialogEnd(){
if (dialogClass!=null && dialogClass.Trim().Length>0){
MonoBehaviour dialogGuiInstance=(MonoBehaviour)GetComponent(dialogClass);
if (dialogGuiInstance!=null){
// Delete the Dialog Gui script
Destroy(dialogGuiInstance);
}
}
}
public void OnDrawGizmos(){
if (conversationAsset!=null){
Gizmos.DrawIcon (transform.position-new Vector3(0,-dialogRange/3,0),"ParleyDialog.png");
}
}
public void OnDrawGizmosSelected () {
if (conversationAsset!=null){
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere (transform.position, dialogRange);
}
}
Unity 5 has some breaking API changes.
instead of the auto converted line that looks something like:
UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent(gameObject, "Assets/Parley Assets/Scripts/Dialog.cs (150,5)", dialogClass);
Unity has a blog post about this api change here
//This will add the dialog, so, And now the window will show up!
gameObject.AddComponent<dialog>();
Since the dialog script is a MonoBehaviour, I added it to a object to display it.
But it doesn't display any windows dialog when i click on the object.
I am trying to create a thread which will continuously check for changes to a value, then visually show that change in a PictureBox located in my GUI.
What I actually wrote is a bit more complicated, so I simplified it while keeping the basic idea, I would be happy to provide clarification if this isn't enough:
public class CheckPictures
{
PictureBox update;
List<String> check;
public CheckPictures(PictureBox anUpdate, List<String> aCheck)
{
update = anUpdate;
check = aCheck;
}
public void start()
{
while(true)
{
if (aCheck[0] == "Me")
{
update.Image = Image.fromFile("");
}
}
}
}
static int Main(string[] args)
{
List<String> picturesList = new List<String>();
CheckPictures thread1 = new CheckPictures(PictureBox1, picturesList);
Thread oThread1 = new Thread(thread1.start));
}
What I want it to do is dynamically change the picture in PictureBox1 if I were to add the string "Me" to pictureList. The above code isn't working like I'd hoped. I had thought that by passing the actual PictureBox and List, any changes to the List elsewhere is the program would be caught by the thread. So my first question is: Is this possible? And if so, what change would I need to make to my code to achieve it?
You might want to use events. You register an eventhandler and when something changes in one thread it calls an event handler in the other to do the work. Busy waiting wastes cpu.
You definetely do not want to do an infinite loop, this will just consume cpu:
while(true)
{
if (aCheck[0] == "Me")
{
update.Image = Image.fromFile("");
}
}
I think you should look into the CountdownLatch class.
public class CountdownLatch
{
private int m_remain;
private EventWaitHandle m_event;
public CountdownLatch(int count)
{
m_remain = count;
m_event = new ManualResetEvent(false);
}
public void Signal()
{
// The last thread to signal also sets the event.
if (Interlocked.Decrement(ref m_remain) == 0)
m_event.Set();
}
public void Wait()
{
m_event.WaitOne();
}
}
The basic idea here is that you need to stop execution on your thread for some time and resume whenever a certain condition has been met (perhaps on another thread).
In other words, you will have a counter, decrement its value on certain condition and whenever it goes to zero you fire your event, execute some code and then start over (stop execution and wait for the counter to go to zero).
In your case you could set the counter to 1 and decrement its value whenever you've set aCheck[0] = "Me"; This way you don't waste CPU.
Pseudo code:
Initialize counter:
CountdownLatch latch = new CountdownLatch(1);
Make thread wait:
public void start()
{
while(true)
{
latch.Wait(); //execution stops
{
//execution resumes once the latch counter is zero.
if (aCheck[0] == "Me") //double check you have what you need
{
update.Image = Image.fromFile("");
latch = new CountdownLatch(1); //reset if you need to do it again
}
}
}
}
Whenever your condition is met (i.e. aCheck[0] = "Me";) signal your latch:
latch.Signal();
this last line will make the thread resume execution. Good stuff.
Create some object, which will raise event, when new picture was added. E.g. class representing pictures collection:
public class PicturesCollection
{
public event EventHandler<PictureAddedEventArgs> PictureAdded;
private List<string> _pictures = new List<string>();
public void Add(string name)
{
_pictures.Add(name);
if (PictureAdded != null)
PictureAdded(this, new PictureAddedEventArgs(name));
}
public IEnumerable<string> Pictures
{
get { return _pictures; }
}
}
If you want to provide some additional data to event, create custom EventArgs:
public class PictureAddedEventArgs : EventArgs
{
public PictureAddedEventArgs(string name)
{
Name = name;
}
public string Name { get; private set; }
}
All you need now - create pictures collection and subscribe to that event:
static int Main(string[] args)
{
PicturesCollection pictures = new PicturesCollection();
pictures.PictureAdded += Pictures_PictureAdded;
}
static void Pictures_PictureAdded(object sender, PictureAddedEventArgs e)
{
if (e.Name == "Me")
PictureBox1.Image = Image.fromFile("");
}
If you add somewhere in your application new picture to collection, it will raise PictureAdded event, which you can handle and update PictureBox. CPU is not wasted in this case.