How do I resolve, get_isActiveAndEnabled can only be called from the main thread error
below is my script that i have attached to an empty game object. I made sure to assign the dropdown and text fields to the respective object accordingly(In the properties inspector).
below is a copy of my class.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
//Access the DarkRift namespace
using DarkRift;
using System;
using System.Net.Sockets;
using System.Timers;
public class Manager : MonoBehaviour {
public Dropdown dropdown;
public Text myText;
public Timer timer;
public string serverSelection = "";
public string serverIP = "";
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void connectButtonClickHandler()
{
print("Connecting to " + serverIP.ToString());
//attempt to connect to the server.
//Connect to the DarkRift Server using the Ip specified (will hang until connected or timeout)
try
{
DarkRiftAPI.Connect (serverIP);
} catch(Exception ex)
{
print("Cannot connect to. " + serverIP.ToString());
print("Message: " + ex.Message);
}
//Setup a receiver so we can create players when told to.
DarkRiftAPI.onDataDetailed += ReceiveData;
//Tell others that we've entered the game and to instantiate a player object for us.
if (DarkRiftAPI.isConnected)
{
print("Connected to server");
//Get everyone else to tell us to spawn them a player (this doesn't need the data field so just put whatever)
//DarkRiftAPI.SendMessageToOthers (TagIndex.Controller, TagIndex.ControllerSubjects.JoinMessage, "hi");
//Then tell them to spawn us a player! (this time the data is the spawn position)
//DarkRiftAPI.SendMessageToAll (TagIndex.Controller, TagIndex.ControllerSubjects.SpawnPlayer, new Vector3(0f,0f,0f));
}
else
print("Failed to connect to DarkRift Server!");
//display a message
myText.text = "Failed to connect to server: " + serverIP.ToString();
//set a timer that makes the message dissapear eventually
timer = new Timer();
timer.Interval = 3000;
timer.AutoReset = false;
timer.Elapsed += onTimerTick;
//start the timer
timer.Start();
//go back to Connect screen
}
public void onTimerTick(System.Object source, ElapsedEventArgs e)
{
print("tick!");
//empty the error text
myText.text = "";
}
public void onServerSelectionChange()
{
print("Changed..!");
if(dropdown.value == 0)
{
//Canada selected
serverSelection = "Canada";
serverIP = "127.0.0.1";
print("Canada selected!");
} else if(dropdown.value == 1)
{
//United States selected
serverSelection = "United States";
serverIP = "google.com";
print("United States selected!");
}
}
void ReceiveData (ushort senderID, byte tag, ushort subject, object data)
{
print("data received!");
//When any data is received it will be passed here,
//we then need to process it if it's got a tag of 0 and, if
//so, create an object. This is where you'd handle most admin
//stuff like that.
//Ok, if data has a Controller tag then it's for us
// if (tag == TagIndex.Controller)
// {
// //If a player has joined tell them to give us a player
//// if (subject == TagIndex.ControllerSubjects.JoinMessage)
//// {
//// //Basically reply to them.
////// DarkRiftAPI.SendMessageToID (senderID, TagIndex.Controller, TagIndex.ControllerSubjects.SpawnPlayer, player.position);
//// }
//
// //Then if it has a spawn subject we need to spawn a player
//// if (subject == TagIndex.ControllerSubjects.SpawnPlayer)
//// {
//// //Instantiate the player
//// //GameObject clone = (GameObject)Instantiate (playerObject, (Vector3)data, Quaternion.identity);
//// //Tell the network player who owns it so it tunes into the right updates.
//// //clone.GetComponent<NetworkPlayer>().networkID = senderID;
////
//// //If it's our player being created allow control and set the reference
////// if (senderID == DarkRiftAPI.id)
////// {
////// //clone.GetComponent<Player>().isControllable = true;
////// // player = clone.transform;
////// }
//// }
// }
}
}
The problem happens in my timer elapsed event handler, on the below line when i try to set the Text's text property. I also have a click button handler in there as you can see.
myText.text = "";
Answer by EuanHollidge · Mar 17, 2017 at 11:22 PM
Very simply from what I can tell you are using another thread to connect to the server. Since unity isn't technically thread compatible, anything other than the usual C# you can't do in a thread. What I would recommend doing is creating a String. Set the string = to what you were setting myText.text to. Then in Update() set myText.text = equal to the string either each frame or have a boolean set up so that it will only update when it changes. Here's the code :
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
//Access the DarkRift namespace
using DarkRift;
using System;
using System.Net.Sockets;
using System.Timers;
public class Manager : MonoBehaviour {
public Dropdown dropdown;
public Text myText;
public Timer timer;
public string serverSelection = "";
public string serverIP = "";
string text;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
myText.text = text;
}
public void connectButtonClickHandler()
{
print("Connecting to " + serverIP.ToString());
//attempt to connect to the server.
//Connect to the DarkRift Server using the Ip specified (will hang until connected or timeout)
try
{
DarkRiftAPI.Connect (serverIP);
} catch(Exception ex)
{
print("Cannot connect to. " + serverIP.ToString());
print("Message: " + ex.Message);
}
//Setup a receiver so we can create players when told to.
DarkRiftAPI.onDataDetailed += ReceiveData;
//Tell others that we've entered the game and to instantiate a player object for us.
if (DarkRiftAPI.isConnected)
{
print("Connected to server");
//Get everyone else to tell us to spawn them a player (this doesn't need the data field so just put whatever)
//DarkRiftAPI.SendMessageToOthers (TagIndex.Controller, TagIndex.ControllerSubjects.JoinMessage, "hi");
//Then tell them to spawn us a player! (this time the data is the spawn position)
//DarkRiftAPI.SendMessageToAll (TagIndex.Controller, TagIndex.ControllerSubjects.SpawnPlayer, new Vector3(0f,0f,0f));
}
else
print("Failed to connect to DarkRift Server!");
//display a message
myText.text = "Failed to connect to server: " + serverIP.ToString();
//set a timer that makes the message dissapear eventually
timer = new Timer();
timer.Interval = 3000;
timer.AutoReset = false;
timer.Elapsed += onTimerTick;
//start the timer
timer.Start();
//go back to Connect screen
}
public void onTimerTick(System.Object source, ElapsedEventArgs e)
{
print("tick!");
//empty the error text
text ="";
}
public void onServerSelectionChange()
{
print("Changed..!");
if(dropdown.value == 0)
{
//Canada selected
serverSelection = "Canada";
serverIP = "127.0.0.1";
print("Canada selected!");
} else if(dropdown.value == 1)
{
//United States selected
serverSelection = "United States";
serverIP = "google.com";
print("United States selected!");
}
}
void ReceiveData (ushort senderID, byte tag, ushort subject, object data)
{
print("data received!");
//When any data is received it will be passed here,
//we then need to process it if it's got a tag of 0 and, if
//so, create an object. This is where you'd handle most admin
//stuff like that.
//Ok, if data has a Controller tag then it's for us
// if (tag == TagIndex.Controller)
// {
// //If a player has joined tell them to give us a player
//// if (subject == TagIndex.ControllerSubjects.JoinMessage)
//// {
//// //Basically reply to them.
////// DarkRiftAPI.SendMessageToID (senderID, TagIndex.Controller, TagIndex.ControllerSubjects.SpawnPlayer, player.position);
//// }
//
// //Then if it has a spawn subject we need to spawn a player
//// if (subject == TagIndex.ControllerSubjects.SpawnPlayer)
//// {
//// //Instantiate the player
//// //GameObject clone = (GameObject)Instantiate (playerObject, (Vector3)data, Quaternion.identity);
//// //Tell the network player who owns it so it tunes into the right updates.
//// //clone.GetComponent<NetworkPlayer>().networkID = senderID;
////
//// //If it's our player being created allow control and set the reference
////// if (senderID == DarkRiftAPI.id)
////// {
////// //clone.GetComponent<Player>().isControllable = true;
////// // player = clone.transform;
////// }
//// }
// }
}
}
actually yes i finally understand what you meant. I got the issue resolved now. I created a variable called message of type String. In update(), i ran the code myText.text = message;
in the timer event handler i changed the code to be the following.
public void onTimerTick(System.Object source, ElapsedEventArgs e)
{
print("tick!");
//empty the error text
message = "";
}
now everything works as expected. thanks.