Pathfinding on a separate thread, how?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Macryc

Dear All,

I have a problem with pathfinding on a separate thread. I saw some similar questions here but none of the answers seem to be working for me - I’m clearly misunderstanding something very basic.

I have a scene with multiple enemies in it which are kinematic bodies that follow paths on a navigation node.

I also have a global script (singleton) with a pathfinding function. Each enemy sends a path calculation request to that function every second or so. I want that function to run on a separate thread. My singleton script is this:

extends Node

var thread
var navigation
var agent

func _ready():
	thread = Thread.new()
	
#this is the function called be each enemy. It's passing in some data
func get_path_to_target(me, target_pos, path, nav):
	agent = me
	navigation = nav
	thread.start(self, "calcpath", target_pos)
	var requested_path = thread.wait_to_finish()
	path = requested_path
	return path

func calcpath(target_pos):
	var pth = navigation.get_simple_path(agent.global_transform.origin, target_pos)
	return pth

This theoretically works but the FPS drops to like 20 and there is a significant stutter each time an enemy requests a path. What am I doing wrong?

:bust_in_silhouette: Reply From: bridsy

When you call thread.start(self, "calcpath", target_pos), your second thread is spawned and starts executing, so now you (kinda) have two things happening at once. However, you then immediately callthread.wait_to_finish() on your main thread, so it goes to sleep and waits for thread 2 to finish. Once it finishes, your main thread wakes back up and continues. This is almost no different to using a single thread because your main thread is just blocked and waiting on your second thread to finish while it executes.

What you probably want to do in this kind of scenario is create a long-running second thread that will execute pathfinding requests for your agents, so your main thread never has to block and wait for it to finish. But be wary that running pathfinding on a separate thread can get complicated.

Fully agree with you here. It’s rather easy to achieve this in C++ using std::future and std:: promise but with GDScript its hard to even get the first letter typed.

Suppose the general approach here would be to have a bucket list in the path finding thread that awakes when requests are added, does the calculations then sends the information back to the agent when completed, after go to sleep when the list is empty.

So Semaphore and Mutex will definitely be needed … Hmm this may actually be easier than expected

Wakatta | 2021-10-13 00:51

:bust_in_silhouette: Reply From: r.bailey

Not sure how intensive your path calculation is, but that could be what the issue is at heart. It could be how you coded the pathing logic, it might not be though. Gdscript is not great if you start doing large calculations within it. And by large It doesn’t even have to be that large, just depends on what you doing. For example if you are using strings take a string and add “A” string on to the end of the string 50000 times.

 var aTest = "A"
 for n in range 50000:
     aTest += "A" 

If you look at how the string class works and the operator around this works in stable you would realize why this is so slow. Of course this is just an example I can think of off the top of my head. The general case of getting around this is to take any functions that need high performance and make either a module for the Godot engine or a GDNative DLL to do the logic in C++. Then you can pass the results back to where ever you need to.

There is a bit of learning curve to all this of course. There was this YouTube video that was made that helped explain some of the process in general and why he did what he did with GDNative. I will link it below, along with GDNative Godot in stable version, to get you started if that is what will help you out.

Using C++ with Godot (GDNative Tutorial) “Ten Thousand Subs”

GDNative C++ example -Godot Stable

Hope this helps lead you into a solution.