- Home /
Create GameObjects in Threads?
So I have a class I'm using in Editor to make a collection of GameObjects. It takes a long time, and there's no update in between items, so it freezes until all items are made. Wanting to speed this up, I moved the code to a separate class, and called that on a thread, but when I run it, I get an error:
Internal_CreateGameObject can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
I'm wondering if there's either a) a means to create these objects in a thread b) a means to update my GUI on the status between GameObject Creations (I've looked at Coroutine but didn't quite get it. c) Application.DoEvents for those familiar with VB.Net.
Answer by MartinCA · Sep 22, 2014 at 03:41 PM
All Unity related objects are NOT thread safe and can either only be accessed from the main thread or will result in unexpected behavior.
You can either use a coroutine or just manage an update state yourself and chunk instantiation. Either way the implementation should look pretty much the same.
For a coroutine approach, you can do something along these lines:
IEnumerator InstantiateOverTime( GameObject prefab, int count, int frames )
{
int itemsPerFrame = Mathf.Floor( count / frames );
int spawned = 0;
while ( spawned < count )
{
for ( int i = 0; i < itemsPerFrame && spawned < count; i++ )
{
Instantiate( prefab );
spawned++;
}
yield return new WaitForEndOfFrame();
}
}
What this does in essense is run the inner loop a specified number of times, once the inner loop completes it hits the yield statement, where it yields execution and returns to execute at the specified time. After returning it will evaluate the outer loop and the process will recur untill all items have been spawned.
You can also achieve the same result by implementing the same behavior inside an Update() call, only instead of the outer loop, you would evaluate if ( spawned < count ) before entering the inner loop.
Do note I haven't tested this code, just writing off the top of my head as I'm having dinner. Might be some typos hiding there or an off by 1 in the math :)
Excellent response. I've tried to understand the Coroutine documentation, but it's not the greatest. I couldn't even tell if this could be or had to be in the same thread as the main. So, if I understand this correctly, the Yield statement basically causes the execution to leave the current subroutine (or closest set of brackets) to go do other stuff, then returns to where it left off?
I'm building a neighborhood, that's a series of blocks, that's a series of SidesOfStreet, that's a series of Buildings that have to be instantiated, textured, rotated, and positioned. So I have nested loops already. If I make the CreateNeighborhood function an IEnumerator type, and place a yield statement right after the house is finished, then it should allow my UpdateGUI function to properly display the status?
$$anonymous$$artinCA: Actually, after trying all day to get this to work, I discovered that Coroutines do not work in the Editor fore a few reasons.
1. They are a part of the $$anonymous$$onoBehavior class, and this is derived from the EditorWindow class. 2. Coroutines don't work in the editor as it only Updates when changes are made, and so the frame changes used in Coroutines are only available at runtime.'
So... Back to the drawingboard. Question still persists. How do you create/modify several objects without locking up the window, or updating a percentbar?
Hmm... $$anonymous$$issed the point where you're trying to do this in the editor - sorry :)
Either way, several things come to $$anonymous$$d - 1. You have an update method for editor windows. 2. If it only executes on redraw, you can force it to run every frame. I've done it before but am currently at work - either way, I think calling setdirty at the end of your frame should force a redraw and update - or maybe you need to register to a system event? Either way I have done it before so the option exists. 3. You could also try instantiating a new GameObject with that logic attached, and add the attribute ExecuteInEdit$$anonymous$$ode to it
All Unity related objects are NOT thread safe and can either only be accessed from the main thread or will result in unexpected behavior.
Actually they can't be accessed from other threads at all. It used to be the case that they could, and you would get the "unexpected behavior" thing, but they closed that off long ago and actively prevent it now. Hence the error message.
By the way, it's almost always better to use "yield return null" rather than creating a new object. It's rare that you actually need to specifically wait until the end of the frame; simply waiting until the next frame is enough.
By the way, it's almost always better to use "yield return null" rather than creating a new object. It's rare that you actually need to specifically wait until the end of the frame; simply waiting until the next frame is enough
Cool, didn't know you could do that.
There are two things that come to $$anonymous$$d regarding the yield return null.
It creates ambiguity in the code, WaitForEndOfFrame is very explicit and understandable whereas null is only clear if you explicitly know it defaults to end of frame.
Does it really prevent an allocation by passing a null argument, or is the scheduler creating a default object in the background? If it does not prevent allocation, I would go with the more expressive declaration - otherwise, I guess it's a performance vs. clarity debate :)
Ins$$anonymous$$d, I tend to create a single WaitForEndOfFrame / WaitForSeconds (before the loop) and just re-use the one instantiated version over and over again.
Realise this is years old, but still pertinent.
Your answer
Follow this Question
Related Questions
How do I call a function in a different CPU core? 2 Answers
EncodeToJPG is too slow. Blocking main thread.Is there new way alternative? 0 Answers
How can I get the number of physical cores in the CPU (not logical cores) 1 Answer
Is it possible to split Texture2D.LoadImage into multiple parts? 1 Answer
CombineMeshes in thread 1 Answer