Hi guys ;)
Im working on a system, for managing spawn/despawn objects on a big map, managed by a thread.
Basically, the script spawn objects that are near enough to camera and frees objects further away from cam.
I have a working system, where a script managing spawn/despawn the same way but also manages 3 LOD levels
all in a single thread, map is divided in chunks. work fine, but take some important millisecs from my mainloop.
so i tried to do it in a seperate thread. after have some troubles first, never bevore used threads, i managed to
get it work.... kind of.
In the code below i just iterate over all mapobjects to test the distance in a seperate thread and it work fine so far.
(i know its not efficient without chunksystem, but that wil come afterwards)
but i would like to spawn the instances in the thread too, and then only add it as child in the main thread (or Deffered).
but that dont work.... after runring the thread for some time, i get various exceptions.
-So, how far can i go with manipulating nodes in thread before i add it to the tree by call AddChild?
-Is this just because of signals wont connect when instanced not in main thread?
-all tipps regarding manipulate nodes in threads (and threading overall in GD) are welcome
-in the docs is not much about this, they only say, i can do all, but not manipulate nodes allready added to the tree.
1st exception
ERROR: Disconnecting nonexistent signal 'changed', slot: 19102:_mesh_changed.
at: (core/object.cpp:1538)
2nd exception
Managed Stacktrace:
=================================================================
at <unknown> <0xffffffff>
at Godot.NativeCalls:godot_icall_1_450 <0x000c4>
at Godot.PackedScene:Instance <0x000c2>
at Godot.PackedScene:Instance <0x00092>
at MTWorldGenerator:CheckMapobjectDistances <0x00a22>
at System.Threading.QueueUserWorkItemCallback:WaitCallback_Context <0x000c5>
at System.Threading.ExecutionContext:RunInternal <0x00646>
at System.Threading.ExecutionContext:Run <0x00082>
at System.Threading.QueueUserWorkItemCallback:System.Threading.IThreadPoolWorkItem.ExecuteWorkItem <0x0013a>
at System.Threading.ThreadPoolWorkQueue:Dispatch <0x00785>
at System.Threading._ThreadPoolWaitCallback:PerformWaitCallback <0x000a2>
at <Module>:runtime_invoke_bool <0x00174>
=================================================================
when i put this part out of the thread, to the main threads CheckForSpawn() method it works!
mo.Instance = mo.Prefab.Instance<Spatial>();
Transform trans = mo.Instance.Transform;
trans.origin = mo.Position;
mo.Instance.Transform = trans;
~~~
MapObjectsToParent[0] will replace mo*
Is not the full class.... sorry but i cant post such long post because of character limit.
Threading script with spawning inside thread (dont work):
~~~csharp
public override void _Process(float delta)
{
if(!LODThreadRun)
{
GD.Print("Run LODThread");
LODThreadRun = true;
ThreadPool.QueueUserWorkItem(new WaitCallback (CheckMapobjectDistances));
}
//Iterate over List, populated by thread, to despawn nodes in main thread
CheckForDespawn();
//Iterate over list, populated by thread, to spawn and position MapObject.Instances
CheckForSpawn();
}
public void CheckForDespawn()
{
//As mentioned above, despawn maximal n MapObject.Instances per frame
//The MapObjectsToDespawn List is populated from thread
int counter = 0;
lock(MapObjectsToDespawn)
{
while(MapObjectsToDespawn.Count > 0)
{
MapObjectsToDespawn[0].HasParent = false;
MapObjectsToDespawn[0].Instance.QueueFree();
MapObjectsToDespawn.RemoveAt(0);
counter++;
if(counter > 10)
{
return;
}
}
}
}
public void CheckForSpawn()
{
//Similar to above method, but for spawn in the MapObject.
//Instance in main thread maximal n per frame
//The MapObjectsToSpawn List is populated from thread
int counter = 0;
lock(MapObjectsToParent)
{
while(MapObjectsToParent.Count > 0)
{
MapObjectsToParent[0].HasParent = true;
GetParent().AddChild(MapObjectsToParent[0].Instance);
MapObjectsToParent.RemoveAt(0);
counter++;
if(counter > 10)
{
return;
}
}
}
}
//Executet in thread as while(true) loop.
//Iterates over all MapObjects on the map and check the distance to the camera.
public void CheckMapobjectDistances(object o)
{
while(true)
{
//Tis is probably wrong to get the cam pos in the thread,
// but works until the programm is ended,
//on last frame it trows a exception, but dont matter yet
Vector3 camPos = cam.GlobalTransform.origin;
//Create 2 lits for spawnable and despawnable objects and
//populates it with Mapobjects to spawn/despawn
List<MapObject> _spawners = new List<MapObject>();
List<MapObject> _despawners = new List<MapObject>();
//Get the list as toarray() in locking state (dont know if i have to lock that)
MapObject[] mapObjectArray;
lock(ListMapObjects)
{
mapObjectArray = ListMapObjects.ToArray();
}
//determine if a MapObject.Instance has to spawn/despawn
foreach(MapObject mo in mapObjectArray)
{
float distance = camPos.DistanceTo(mo.Position);
if(distance > MaxDistanceToCam)
{
if(mo.HasParent && mo.IsSpawned)
{
mo.IsSpawned = false;
_despawners.Add(mo);
}
}
else
{
if(!mo.IsSpawned && !mo.HasParent)
{
mo.IsSpawned = true;
mo.Instance = mo.Prefab.Instance<Spatial>();
Transform trans = mo.Instance.Transform;
trans.origin = mo.Position;
mo.Instance.Transform = trans;
_spawners.Add(mo);
}
}
}
//add the new MapObjects to fill it in lists, processed by main thread in locked states
lock(MapObjectsToParent)
{
foreach(MapObject mo in _spawners)
{
MapObjectsToParent.Add(mo);
}
}
lock(MapObjectsToDespawn)
{
foreach(MapObject mo in _despawners)
{
MapObjectsToDespawn.Add(mo);
}
}
}
}
}
~~~
The MapObject
public class MapObject
{
public PackedScene Prefab;
public Vector3 Position;
public bool IsSpawned = false;
public bool HasParent = false;
public Spatial Instance;
}