One of the options is using the Dispatcher pattern. The idea of this pattern is to create an object for each thread you want to invoke on(main thread in your case) which maintains list of delegates that is being polled and executed(e.g. in Update
method). Since there is notno implementation in Unity I am aware of, you either need to find Dispatcher implementation for Unity or implement it by yourself.
A very minimalist example
public class Dispatcher : MonoBehaviour
{
protected Queue<Action> _pending = new Queue<Action>();
void Update()
{
lock(_pending)
{
while(_pending.Count != 0) _pending.Dequeue().Invoke();
}
}
public void Invoke(Action a)
{
lock (_pending) {_pending.Enqueue(a);}
}
}
//spawning enemies from a new thread
public class Spawn: MonoBehaviour
{
private Dispatcher _dispatcher;//init in Start() or editor
void asyncSpawn()
{
for (int i = 0; i < _targetEnemyCount; i++)
{
Thread.Sleep(1000);
_dispatcher.Invoke( () => spawnEnemy());
}
}
void Start()
{
new Thread(() => asyncSpawn()).Start();
}
}