- Home /
Proper use of threads with procedural generation
Hi everybody! I have been working on a simple chunk based terrain generation system, with voxel octrees. The overall method works pretty well, but testing this system with large amounts of chunks completely freezes the game/main thread until the generation process is finished. Looking for a solution, I read about threads and coroutines and I implemented a simple thread class (based on this template) to call the "generate" function from it. The only advantage I had was a faster generation, with full CPU usage, but the game still freezes until the chunks are completely loaded. Am I doing something wrong? Here some parts of the code:
public class ChunkThread : ThreadedJob {
public Chunk chunk;
protected override void ThreadFunction()
{
chunk.root = chunk.root.BuildOctree (chunk.pos, Chunk.size, -1);
}
protected override void OnFinished()
{
chunk.RenderChunk ();
}
}
This is the simple Thread class I use to call the Build function, and the Render function when finished. This piece of code below is the start\update method for every Chunk gameObject (I kept only the relevant part), that simply creates the Thread instance and calls Start() pressing the mouse button.
void Start () {
t = new ChunkThread ();
t.chunk = this;
}
void Update () {
if (Input.GetButtonDown ("Fire1")) {
t.Start ();
}
Now, coming to the questions, what should I do to generate the terrain while still being able to play? Is there something wrong in my thread? Should I use a thread pool, if so how?
I used this template to build my thread class (it should be linked in the question). Anyway to avoid any doubt this is the ThreadedJob class
public class ThreadedJob
{
private bool m_IsDone = false;
private object m_Handle = new object();
private System.Threading.Thread m_Thread = null;
public bool IsDone
{
get
{
bool tmp;
lock (m_Handle)
{
tmp = m_IsDone;
}
return tmp;
}
set
{
lock (m_Handle)
{
m_IsDone = value;
}
}
}
public virtual void Start()
{
m_Thread = new System.Threading.Thread(Run);
m_Thread.Start();
}
public virtual void Abort()
{
m_Thread.Abort();
}
protected virtual void ThreadFunction() { }
protected virtual void OnFinished() { }
public virtual bool Update()
{
if (IsDone)
{
OnFinished();
return true;
}
return false;
}
private void Run()
{
ThreadFunction();
IsDone = true;
}
}
I'd like to help, but I'm totally unfamiliar with the ThreadedJob base class your using. I couldn't even find docs on it, did you create it yourself? Is it possible the issue lies with this class (and if so, can you post that code too)?
However, I can say it LOO$$anonymous$$s like you are doing it right, (I'm assu$$anonymous$$g ThreadedJob.Start() actually launches the thread with the code in the function "ThreadFunction").
You should NOT need a thread pool for this, at least to get started.
Remember, you may NOT call any unity API functions in your thread! (not that you should need it to generate an octtree).
Here is a sample that I whipped on on how to launch a thread without the ThreadedJob class, just using the basic Thread class. Perhaps using something based on this will give you better results? (this sample calls the thread every update cycle, if it's not already running- you'll obviously want to change that to work on a mouse button press or something)
using UnityEngine;
using System.Collections;
using System.Threading;
public class ThreadedCube : $$anonymous$$onoBehaviour {
static Thread testThread;
static public string some_value="0";
// Use this for initialization
void Start () {
some_value = "0";
testThread = new Thread(ThreadFunc);
}
// Update is called once per frame
void Update () {
Debug.Log(some_value.ToString());
if (!testThread.IsAlive)
{
testThread = new Thread(ThreadFunc);
testThread.Start("test");
}
}
static void ThreadFunc(object textObject)
{
some_value = (string)textObject;
}
}
Answer by Glurth · Mar 14, 2015 at 03:15 PM
oops! You merged that link so smoothly, I totally missed it, sorry bout that.
That class looks ok to me, and looks like you are using it correctly. Hmmm, the code obviously being called, that's why the program hangs, but why is it hanging if in another thread...
Ah! here is a possibility: You may need to insert some Thread.Sleep(0); calls into your OctTree generator function. This allows the main thread some time to run it's processes, before returning to yours.