Cant instantiate a prefab from another script - c#

I cant instantiate a prefab from another script, it does nothing and gives me no errors. In the same "play" i can instantiate the same prefab from the same script multiple times but if i try to do it by executing the same method from another script it doesnt work.... i pass some variables and print them ( it work) but instatiation is doing nothing....please help!
I tried to solve it by:
public GameObject reference,
UnityEvents and
Ataching both scripts in one object
Script One
void OnEnable()
{
EventSystemManager.StartListening("printMessage", AddMessage);
}
public void AddMessage(string message )
{
GameObject remoteMessage = Instantiate(localMessagePrefab,
chatTransform);
remoteMessage.transform.GetChild(0).GetComponentInChildren<Text>
().text = message;
}
Script Two
public IEnumerator StartWebSocket()
{
using (ws = new WebSocket(serveradress))
{
ws.OnOpen += (sender, e) =>
onOpenHandler();
ws.OnMessage += (sender, e) =>
onMessageHandler(e.Data);
ws.OnError += (sender, e) =>
onErrorHandler(e);
ws.OnClose += (sender, e) =>
onCloseHandler(e);
ws.Connect();
for (; ; )
{
yield return null;
}
}
}
private async void onMessageHandler(string e)
{
serverMessage = JsonUtility.FromJson<serverMssg>(e);
EventSystemManager.TriggerEvent("printMessage",
serverMessage.normmsg);
await miaApi.queueAnim(serverMessage.normmsg);
}
The message is passed but instantiation do nothing....
The only thing i detect in debugging is that transform dissapears when tryng to do it from other script:
from the same script:
from another script:
Also there is a video (without sound)
The video doesnt use events but is exactly the same behavior
Video
TIA!

I don't know how the EventSystemManager works.
In case the issue is actually the async and multi-threading you could try a simple "dispatcher" for passing callbacks back to the main thread:
public class MainThreadDispatcher : MonoBehaviour
{
private static MainThreadDispatcher _instance;
public static MainThreadDispatcher Instance
{
get
{
if(!_instance)
{
_instance = new GameObject("MainThreadDispatcher").AddComponent<MainThreadDispatcher>();
DontDestroyOnLoad(_instance.gameObject);
}
return _instance;
}
}
private ConcurrentQueue<Action> actions = new ConcurrentQueue<Action>();
private void Update()
{
// work the queue in the main thread
while(actions.TryDequeue(out var action))
{
action?.Invoke();
}
}
public void Dispatch(Action action)
{
actions.Enqueue(action);
}
}
and then use it to make sure the callback is done in the main thread
private MainThreadDispatcher mainThreadDispatcher;
void OnEnable()
{
// before dispatching stuff make sure an instance reference is optained
// or is created in the main thread
mainThreadDispatcher = MainThreadDispatcher.Instance;
EventSystemManager.StartListening("printMessage", AddMessage);
}
// might be executed in a thread
public void AddMessage(string message )
{
// instead of directly executing the code wait for the main thread
// either using a lambda expression
mainThreadDispatcher.Dispatch(() =>
{
GameObject remoteMessage = Instantiate(localMessagePrefab,
chatTransform);
remoteMessage.transform.GetChild(0).GetComponentInChildren<Text>
().text = message;
});
// or if you prefer a method
//mainThreadDispatcher.Dispatch(HandleAddMessage);
}
// should be only executed in the main thread
private void HandleAddMessage()
{
GameObject remoteMessage = Instantiate(localMessagePrefab,
chatTransform);
remoteMessage.transform.GetChild(0).GetComponentInChildren<Text>
().text = message;
}

Related

Unity Coroutine is not starting when called from an event handler

I am having a problem while trying to call StartCoroutine from the body of an event handler. I am expecting to print the following on the console, instead it only prints "before" and it does not start the coroutine:
before
inside
working
after
private void Awake()
{
socket.OnMessage += (sender,e) => {
Debug.Log("before");
StartCoroutine(handleIncommingMessages(sender,e));
Debug.Log("after");
};
}
public IEnumerator handleIncommingMessages(object sender, MessageEventArgs e) {
Debug.Log("inside");
yield return new WaitForSeconds(3f);
Debug.Log("working");
}
This coroutine only works in main thread. You have to inject the method of the coroutine you want to execute to the main thread.
Usually when you try to run it in another thread you get this error:
UnityException: IsObjectMonoBehaviour can only be called from the main thread.
I solved this by defining a public List and using Update method.
List<Action> threadPumpList = new List<Action>();
private void Update()
{
while (threadPumpList.Count > 0)
{
threadPumpList[0].Invoke();
threadPumpList.RemoveAt(0);
}
}
public void WriteTestMessage()
{
Debug.Log("Hello World.")
}
public void Test()
{
threadPumpList.Add(() => WriteTestMessage());
}
You can use this like this.
List<IEnumerator> threadPumpList = new List<IEnumerator>();
private void Update()
{
while (threadPumpList.Count > 0)
{
StartCoroutine(threadPumpList[0]);
threadPumpList.RemoveAt(0);
}
}
private void Awake()
{
//socket.OnMessage += (sender,e) =>
new Thread(() => //You should use socket right here.
{
Debug.Log("before");
threadPumpList.Add(handleIncommingMessages(null, ""));
Debug.Log("after");
}).Start();
}
public IEnumerator handleIncommingMessages(object sender, string e) //I changed parameters for testing.
{
Debug.Log("inside");
yield return new WaitForSeconds(3f);
Debug.Log("working");
}
When I test it the output is like this:
-before
-after
-inside
(3 seconds wait)
-working
There might be issues with the Coroutine being discarded once the event call finishes. I do not know how to fix it - but if you are using a newer version of Unity you can use tasks instead:
private void Awake()
{
// We add async so that we can use the `await` call.
socket.OnMessage += async (sender,e) => {
Debug.Log("before");
// Force it to await when the incoming message as not to close the thread
await handleIncommingMessages(sender, e);
Debug.Log("after");
};
}
public async Task handleIncommingMessages(object sender, MessageEventArgs e) {
Debug.Log("inside");
await Task.Delay(1000);
Debug.Log("working");
}
If that doesn't work try:
public async void handleIncommingMessages instead of Task

How to repeatedly execute a method until said otherwise

I have an EventSystem for managing my turn-based game in Unity.
public class EventSystem : MonoBehaviour
{
private static List<Action> _commandsQueue = new List<Action>();
private bool _canExecuteCommand = true;
public void AddToQueue(Action command)
{
_commandsQueue.Add(command);
}
private void StartCommandExecution()
{
_commandsQueue[0]();
_canExecuteCommand = false;
}
public void CommandExecutionComplete()
{
_canExecuteCommand = true;
}
public void PlayFirstCommandFromQueue()
{
if (_commandsQueue.Any() && _canExecuteCommand)
{
StartCommandExecution();
}
else
{
Debug.LogError("No Command In Queue");
}
}
}
How do I put a method in Update() until _canExecuteCommand is true again but only for some methods?
It is quite broad what you are trying to do but in general you would use an endless loop within a Coroutine.
You can create a generic routine which invokes any Action you pass in as parameter once a frame like e.g.
private IEnumerator InvokeEveryFrame(Action action)
{
// This looks strange but is okey in a Coroutine as long as you yield somewhere within
while(true)
{
action?.Invoke();
// This tells Unity to "pause" this routine here
// render the current frame and continue from here in the next frame
yield return null;
}
}
So all that's left is starting the routine using MonoBehaviour.StartCoroutine like e.g.
Coroutine routine = StartCoroutine(SomeParameterlessMethod);
or if you need parameters
Coroutine routine = StartCoroutine(() => SomeMethod(x, y, z));
and then at some point later stop it using MonoBehaviour.StopCoroutine and the stored Coroutine reference like e.g.
StopCoroutine(routine);
how exactly you store that reference is up to you of course up to you.

Execute method outside Task.run

Call a method outside of Task.Run(). I have this code
private void StartListening(int numPort, int tasknumber)
{
udpClients[tasknumber] = new UdpClient();
udpClients[tasknumber].Client.Bind(new IPEndPoint(IPAddress.Any, numPort));
iPEndPoints[tasknumber] = new IPEndPoint(0, 0);
Task.Run(() =>
{
while (true)
{
AddUniqueRoomList(iPEndPoints[tasknumber].Address.ToString(), Encoding.UTF8.GetString(udpClients[tasknumber].Receive(ref iPEndPoints[tasknumber])));
}
});
}
The code waits for a broadcast message then send a string to AddUniqueRoomList. What I want is just send a message to AddUniqueRoomList method without including it in Task.Run. AddUniqueRoomList method creates a UI button which causes an error if it's inside a task.run() cause unity doesn't do well with multithreading.
As in the comments you can use an often referred to as "dispatcher" in order to let any Action be executed in the Unity main thread.
The simplest way is actually to directly use a thread-save ConcurrentQueue<Action> which doesn't require locks like
public class MainThreadDispatcher : MonoBehaviour
{
// Singleton pattern
private static MainThreadDispatcher _instance;
// This thread-save queue stores Actions to be executed in the main thread
// any thread can add Actions to it via the ExecuteInUpdate method
// then the existing actions will be executed in the Update loop
// in the same order they came in
private readonly ConcurrentQueue<Action> actions = new ConcurrentQueue<Action>();
// This may be called by threads
public static void ExecuteInUpdate(Action action)
{
if(!_instance)
{
Debug.LogError("You can talk but nobody is listening../nNo MainThreadDispatcher in scene!");
return;
}
// Add the action as new entry at the end of the queue
_instance.actions.Enqueue(action);
}
// Initialize the singleton
private void Awake()
{
if(_instance && _instance != this)
{
// an instance already exists so destroy this one
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(this);
}
// This will be executed when the game is started
// AFTER all Awake calls
// See https://docs.unity3d.com/ScriptReference/RuntimeInitializeOnLoadMethodAttribute.html
[RuntimeInitializeOnLoadMethod]
private static void Initialize()
{
// If the instance exists already everything is fine
if(_instance) return;
// otherwise create it lazy
// This adds a new GameObject to the scene with the MainThreadDispatcher
// component attached -> this will call Awake automatically
new GameObject("MainThreadDispatcher", typeof(MainThreadDispatcher));
}
// Executed in the Unity main thread
private void Update()
{
// Every frame check if any actions are enqueued to be executed
while(actions.TryDequeue(out car action)
{
action?.Invoke();
}
}
}
Put this script on any active object in the scene so it's Update is allways called.
Then in your script you wouldn't execute the method directly but rather add it to this queue by doing
private void StartListening(int numPort, int tasknumber)
{
udpClients[tasknumber] = new UdpClient();
udpClients[tasknumber].Client.Bind(new IPEndPoint(IPAddress.Any, numPort));
iPEndPoints[tasknumber] = new IPEndPoint(0, 0);
Task.Run(() =>
{
while (true)
{
var receivedString = Encoding.UTF8.GetString(udpClients[tasknumber].Receive(ref iPEndPoints[tasknumber]));
var address = iPEndPoints[tasknumber].Address.ToString();
MainThreadDispatcher.ExexuteInUpdate(
() =>
{
AddUniqueRoomList(address, receivedString);
}
);
}
});
}

Unity editor freezes when I try to debug my multi threaded app

Whenever I attach the debugger to unity editor, the editor simply freezes as soon as I press the run button.
On Receiving a packet from my server I try to run an async task which calls a static method on my 'screen' script that implements MonoBehavior
public override async Task RunImpl()
{
LoginScreen.UpdateSrvText();
}
LoginScreen looks as follows
public class LoginScreen : MonoBehaviour
{
private static LoginScreen instance = null;
public TextMeshProUGUI ServerResponseText;
private NetworkClient Client;
private static List<System.Action> actionQueuesUpdateFunc = new List<Action>();
private volatile static bool noActionQueueToExecuteUpdateFunc = true;
List<System.Action> actionCopiedQueueUpdateFunc = new List<System.Action>();
void Awake()
{
instance = this;
DontDestroyOnLoad(gameObject);
}
void Start()
{
Client = GlobalControl.Instance.NetworkingClient = new NetworkClient();
}
void Update()
{
ProcessUpdateQueue();
}
public static void UpdateSrvText()
{
instance.UpdateSrc();
}
public void UpdateSrc()
{
ExecuteInUpdate(() =>
{
ServerResponseText.text = GlobalControl.Instance.LoginScreenServerText;
});
}
public static void ExecuteInUpdate(System.Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
lock (actionQueuesUpdateFunc)
{
actionQueuesUpdateFunc.Add(action);
noActionQueueToExecuteUpdateFunc = false;
}
}
private void ProcessUpdateQueue()
{
if (noActionQueueToExecuteUpdateFunc)
{
return;
}
actionCopiedQueueUpdateFunc.Clear();
lock (actionQueuesUpdateFunc)
{
actionCopiedQueueUpdateFunc.AddRange(actionQueuesUpdateFunc);
actionQueuesUpdateFunc.Clear();
noActionQueueToExecuteUpdateFunc = true;
}
for (int i = 0; i < actionCopiedQueueUpdateFunc.Count; i++)
{
actionCopiedQueueUpdateFunc[i].Invoke();
}
}
}
I tried to attach the debugger after I start the app, but then visual studio freezes and the editor. I am trying to reuse the code I found in one of the more popular questions here about implementing simple queue to the main thread, and it is working except for the debugger.

Create events that fires off a coroutine from another script

I'm making an event that does the failLevel stuff when it fires off. For that I have made a delegate
public delegate Coroutine FailGame(IEnumerator function);
public static event FailGame gameFailedEvent;
like so and I subscribed the appropriate function to it
void Start ()
{
gameFailedEvent += StartCoroutine;
}
It works when it is called from the same script like so:
gameFailedEvent(WaitThenFailLevel());
when this WaitThenFailLevel() looks like this:
IEnumerator WaitThenFailLevel()
{
CharacterController2D.playerDied = true;
if (CharacterController2D.animState != CharacterController2D.CharAnimStates.fall)
{
CharacterController2D.currentImageIndex = 0;
CharacterController2D.animState = CharacterController2D.CharAnimStates.fall;
}
CharacterController2D.movementDisabled = true;
yield return new WaitForSeconds(0.2f);
StartCoroutine(ScaleTime(1.0f, 0.0f, 1.2f));
}
It works fine here. Now, I have another object that can kill the player (dangerous times I know) and I don't want to copy paste everything again, I just want it to fire off the static event made in the script above.
I DID try making the WaitThenFailGame function
public static
and make static all my other ienumerators but I got an error named "An object reference is required for non-static field..."
Hence I tried the event stuff.
All well and fine, but I can't call the event from the other script because I can't pass it the function from the script mentioned.
What to do now?
Here is the example code:
EventContainor.cs
public class EventContainer : MonoBehaviour
{
public static event Action<string> OnGameFailedEvent;
void Update()
{
if (Input.GetKey(KeyCode.R))
{
// fire the game failed event when user press R.
if(OnGameFailedEvent = null)
OnGameFailedEvent("some value");
}
}
}
Listener.cs
public class Listener : MonoBehaviour
{
void Awake()
{
EventContainer.OnGameFailedEvent += EventContainer_OnGameFailedEvent;
}
void EventContainer_OnGameFailedEvent (string value)
{
StartCoroutine(MyCoroutine(value));
}
IEnumerator MyCoroutine(string someParam)
{
yield return new WaitForSeconds(5);
Debug.Log(someParam);
}
}
using UnityEngine;
public class ScriptA : MonoBehaviour
{
public ScriptB anyName;
void Update()
{
anyName.DoSomething();
}
}
using UnityEngine;
public class ScriptB : MonoBehaviour
{
public void DoSomething()
{
Debug.Log("Hi there");
}
}
This is linking functions between scripts , Copied from Here, maybe coroutines are the same, Then you need to start the coroutine in void Start() {} , You may find this useful as well.

Categories

Resources