I am looking for a method that allows me to run a process that takes a while in unity without stalling it?
e.g. If I create a map procedural it might take 30 sec to create everything. This causes Unity to stall completely.
I tried using threads but I rely on using Unity methods which can’t deal with multiple threads. Maybe using IEnumerable and Coroutines is better? Using it seems to slow down everything a lot.
What is the proper way to do something like that? Does anyone have experience with this?
There is no elegant way if you don’t use Unity’s Cloud Build Services or a massively powerful and parallelized home or business computer that can more as do the same. I wait like 30 minute on my 2011 2nd Gen Intel i5 CPU with 8 GB RAM for 30 seconds sounds pretty good to me.
I create new GameObjects and work with MonoBehaviours. They can’t be accessed by multiple threads, can they? The last time I tried that unity politely refused to be accessed from anything else than the main thread.
Profiler is a good idea. Start with the profiler to see what is actually causing you issues.
If its the issue is in heavy processing of data, then threading is the solution. Threading is great for running tasks that take longer then 16.67 ms to run, but don’t need to be run every frame. All of your generation stuff comes into this category.
If the issue is in lots of instantiate and destroy calls, or something else that relies on the Unity API, then you need something more clever. Coroutines are a great way to spread the workload across frames. But often this is not enough on its own. You will also need to investigate things like object pooling, combining meshes, and using voxels instead of GameObjects.
Sorry for being not clear enough before. It isn’t really about a specific problem but rather about how to implement this in the future with different tasks that might run “in the background” but need access to unity. The main problem is how to do things in the background without letting the FPS drop below 60.
I can’t find proper ways in Unity to do this so far so Ryiah might be right and I should remove my dependency to unity and use another thread. But then again why do I use unity in the first place? So I hope I still find a way to do it without avoiding unity.
Here is my current approach by using Coroutines and I marked the Problems 1-3 in code comments.
Problem1:
is WaitForEndOfFrame the right place for heavy work?
Problem2:
How much time can I spend working?
Depending on the configuration Unity might just spend the time I don’t use with sleeping to get a 60 FPS target framerate (this is probably where the slowdown is coming from)
Problem3:
DateTime is not accurate enough for this task.
Unitys Time class doesn’t seem to refresh during the frame
How to accurately measure the time to not use too little / too much time for the background task
Despite these problems the solution works kind of… but they cause the FPS to drop on slow machines as I always use 15 ms of (16.6 ms I have if I want 60 fps) for calculating and some machines need a lot for rendering/ the game logic itself. Also DateTime uses 10 and sometimes 20 or even 30 ms for the background task as it isn’t accurate enough for this task.
using UnityEngine;
using System;
using System.Collections;
public class CoroutineTest : MonoBehaviour
{
private bool mRunning = false;
void Start ()
{
StartCoroutine(LotsOfWork());
}
private IEnumerator LotsOfWork()
{
mRunning = true;
while (mRunning)
{
//Problem1:
yield return new WaitForEndOfFrame();
//Problem2:
//how much time can I spend working?
//I have no idea how much the rest of the frame might take so
//how to decide how long I can work?
int workingDuration = 15; //in ms
DateTime startWork = DateTime.Now;
do
{
//do at least one piece of work
//user can't benifit from good fps if the work takes forever...
bool workIsDone = SimulateAPieceOfWork();
if(workIsDone)
{
mRunning = false;
break;
}
} //Problem 3: DateTime is not accurate enough. Is a better way to time this? (without going into C++ as it needs to be platform independend
while ((DateTime.Now - startWork).TotalMilliseconds < workingDuration);
Debug.Log("Worked for " + (DateTime.Now - startWork).TotalMilliseconds);
}
}
/// <summary>
/// This will be replaced with the actualy work later
/// </summary>
/// <returns>returns true if the work is finished</returns>
private bool SimulateAPieceOfWork()
{
//how much work can I do?
int workResult = 0;
for (int i = 0; i < 10000; i++)
{
//do some work here. no idea how long it will take on iphone, webgl, and so on ...
workResult += i;
}
return false;//for test purposes the work is never done
}
}
Edit: I am used to being in control of the main loop of the game. In this case I wouldn’t run into this problem in the first place but I guess you can’t do that with unity
I’ve often seen a stop watch used in this purpose to get the time accurate enough for purposes.
However splitting your heavy data functions off from Unity is a good idea. Think of it in terms of MVC. Unity fits the bill well for your view and controller. But sometimes having the model separate from the Unity API is not a bad thing.
Using stop watches was a good advice. Works much better now! Thank you. It seems there isn’t a really elegant solution though except just not using unity for that. Hoped the unity developers planed those things in
You are right about the MVC pattern. I am just at the end of my current project and don’t want to make any big changes anymore. I used MVC but I made the mistake of accessing unity in all parts of the program e.g. for checking if two objects overlap. I will change this in further projects and use unity for graphics/audio only.