I'm working on a 2D Platformer. I have several KinematicBody2D objects that have several Raycasts attached to get more accurate collisions since I wasn't able to get good pixel accuracy with boxes/areas. When putting around 50 of these bodies on the screen the game sees slow down. I've determined that this is due to the raycasting code. I assumed that having several raycasts should be less expensive than even doing 1 area or 1 collision box? I need be able to put at least 100 of these bodies on the screen at a time without any slow down. Any help would be great, and I'm happy to provide code if needed.
Are Raycast Operations in Godot More Expensive Than Collision Boxes/Areas?
- Edited
A rectangle intersection test is cheap, a ray/rectangle intersection is more expensive.
A single ray-cast depends on what it does, what info it transports, and what it collides with. But 50 shouldn't cause visible performance issues.
Thanks for the response! Just to clarify, do you mean a rectangle intersecting another rectangle is cheap, but a ray intersecting with a rectangle is more expensive?
Here's the code I'm dealing with. Just this is causing slow down. But I should mention I have 8 raycasts running on each of these 50 objects. So per frame this code runs 8 times.
static func get_object_colliders(instigator, raycasts, space_state):
var results = []
var colliders = []
for raycast in raycasts:
while true:
var result = space_state.intersect_ray(raycast.global_position, raycast.global_position + raycast.cast_to,[instigator] + colliders, raycast.collision_mask)
if result.has("collider"):
results.append(result)
colliders.append(result["collider"])
else:
break;
return results
Now, what's curious is just running this on 50 objects with 8 raycasts each and getting NO collisions (so basically casting but not actually colliding with anything) is causing huge, unplayable slow down. The reason I decided on so many raycasts per object is because with other collision boxes/areas I couldn't seem to get pixel accurate collisions. I'm happy to change methods where I can as long as I can get accurate collisions and no slow down! Thanks again!
- Edited
Not sure I understand why you have 'while true:' there, as imagine if a collision occurred it would just repeat endlessly?
Try with this?
for raycast in raycasts:
var result = space_state.intersect_ray(raycast.global_position, raycast.global_position + raycast.cast_to,[instigator] + colliders, raycast.collision_mask)
if result.has("collider"):
results.append(result)
colliders.append(result["collider"])
Also, 8 ray casts on 50 objects would be 400/frame unless you are offsetting frames per object (i.e.
Objects 1-10 check on frames 1,6,11,16
Objects 11-20 check on frames 2,7,12,17
Objects 21-30 check on frames 3,8,13,18
Objects 31-40 check on frames 4,9,14,19
Objects 41-50 check on frames 5,10,15,20)
This would improve performance, but result in checks only occurring ~ 12 times a second, which may not be suitable depending on your use case.
Finally, (maybe not a thing with 2D) you can limit the amount of queries with spatial hashing, effectively culling out checks that would occur for objects beyond a set distance away.
The 'while true:' allows this piece of code to account for multiple collisions in the raycast in one frame. If you look I add the object to a list of colliders that then get put in the ignore section of the raycast. So if it found a collision then it would basically cast again and ignore the collision it hit the first time and look for another one and do this until it found all the collisions that it hit that frame and put them in 'results'.
That being said, I have tried it without the looking for multiple collisions per frame just to see what it did. Since nothing is colliding with anything to begin with all 50 objects not colliding with anything, it just does one raycast and ends. Even without any colliding happening at all I still get massive slowdown.
The offsetting frames per object is interesting, I'd definitely have to think about that more. Do you know if there's a better way to get pixel accurate collisions in Godot other than raycasting? I assume that its likely I'm using excessive amount of raycasts that Godot can't handle it.
Ah yep, missed the mask for recasting, so that makes sense. I don't really ever do 2D so can't speak too much as to what the 'best' process is, but I believe the 2D Physics backend supports multithreading, so pooling to a thread pool should see a pretty good improvement.
Also, if each object has its own _process(), you may have a bad time as believe there is an overhead associated with this that hurts at scale. I've seen improvements before by adding a script to a parent node only that every frame iterates its children to run their logic.