godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.07k stars 69 forks source link

Allow intersect_ray to collect multiple results. #9059

Open JeckDev opened 5 months ago

JeckDev commented 5 months ago

Describe the project you are working on

3D space narrative game

Describe the problem or limitation you are having in your project

Currently, in GDScript, intersect_ray is the only one of the DirectSpaceState3D intersection tests that can not take a max_results parameter. Raycasts only return the first (nearest) hit.

For object picking, I'd like to gather a list of candidate objects under the mouse and then use some code to narrow down which candidate is correct. To do this, I need the surface contact points, so I can't use intersect_shape with a box.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

The best solution would be to expand intersect_ray to take a max_results parameter. This would put it into alignment with the other physics intersect functions.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

To prevent breaking the api, this change would have to not modify existing return values or function calls. Changing the call signature is easy, since we can just make max_results an optional parameter defaulting to 32, like it is for every other intersection function.

Returning multiple results without breaking the API is also possible. We'd simply return the usual dictionary containing the info for the first intersection, but with an additional entry: "all_contacts" containing an array of all results (in the usual dictionary format).

This leaves the GDScript API unchanged for existing users, but expands the functionality to match other intersection commands.

On the engine side, we'd move intersect_ray's code into a modified call signature:

bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parameters, RayResult *r_results, int p_result_max) 

To prevent breaking the engine API, we'd write a wrapper function with the existing call signature:

bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parameters, RayResult &r_result) {
    return intersect_ray(p_parameters,&r_result,1)
}

If this enhancement will not be used often, can it be worked around with a few lines of script?

A shape-cast with a box can also return multiple object intersections, but will not return the contact points.

The best workaround in GDScript at the moment is to first run intersect_shape with a box, then gather contact points by iterating over all results. You'd build an exclusion list containing every intersect_shape result except one collider, then perform a raycast to get the contact point for that target, repeating until you have contacts for each collision.

However, this is cumbersome (requiring the user to write this logic themselves) and non-performant (checking the physics multiple (n=collisions) times for what should be a single check).

Is there a reason why this should be core and not an add-on in the asset library?

Ray intersections are core.

theraot commented 5 months ago

I'd rather have a separate intersect_ray_multiple or by some other name, and not have the "all_contacts" shenanigans proposed, but actually return an Array directly. Is there any reason why you rather modify intersect_ray?

JeckDev commented 5 months ago

I was wanting to make it so there's only one intersect_ray function in the engine to be maintained going forward, and I didn't want to introduce major API differences between GDScript/C++ or to compromise the existing GDScript API. One thing I really appreciate about Godot as someone new to the engine is how the script and the engine follow similar calling conventions, so learning the architecture of one leads to improved intuition about the other.

But honestly as long as there's not two engine code paths to be maintained I don't have strong opinions on how the script access looks.