godotengine / godot-proposals

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

Allow RayCast to report multiple collision points through different objects #8888

Open MertGameDev opened 5 months ago

MertGameDev commented 5 months ago

Describe the project you are working on

assasin's creed style parkour/action game

Describe the problem or limitation you are having in your project

the ray only colides once and it is the closest to the origin of the ray.

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

can we get a ray cast that will collide through multiple objects getting more than one collision point from a raycast I need this make it an option or another ray object

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

it should work like an area does but in a line so I can shoot through multiple enemies and can find the lowest ledge or the highest. Something like this

    var overlaps = $Ray.get_collision_points()
    var closestDistance = 1000
    if overlaps.size() > 0:
        for overlap in overlaps:
            if overlap.is_in_group("Enemy"):
                var distance = overlap.global_transform.origin.distance_to(global_transform.origin)
                if distance < closestDistance:
                    closestDistance = distance
                    closestEnemy = overlap
                if closestEnemy != null:
                    return true

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

Make it so it is a tick in a ray or another ray object.

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

it is not just for the game I am making right now cause it will be in use always for my games. And no one needed it or makes an 3rd person game.

Calinou commented 5 months ago

PS: Code blocks should use triple backticks like this (with an optional language name for syntax highlighting):

```gdscript code here ```

I edited your post accordingly, but remember to do this in the future :slightly_smiling_face:

smix8 commented 5 months ago

As mentioned in that thread that Calinou linked, if you want to collect x amount of colliders for the same collision mask with a ray "line" use a loop and populate the exclude Array of the parameters. That is 1 extra line of code for the Array push_back compared to what you already do for a normal ray cast in script. Also why would you want to loop through your enemies to find the closest ledge, use a different collision_mask for your ledges.

I understand that a lot of the problems that you face come from using a RayCast node. That node is very limited with updates and inefficient for multi-raycasts. The node is intended for really simple single physics ray cases and should not be used for more complex things. When you need many raycasts do the raycasts in scripts and reuse the same physics query parameters.

var parameters := PhysicsRayQueryParameters3D.new()

func _physics_process(delta: float) -> void:
    var raycast_node: RayCast3D = $RayCast3D
    parameters.from = raycast_node.global_position # Add global_position of your RayCast node.
    parameters.to = raycast_node.to_global(
        raycast_node.target_position
        ) # Add global position of your RayCast node local target_position.

    # Create a bitmask that only has Layer3 "ledge" colliders.
    var _bitmask: int = 0 # Create empty bitmask.
    var _bit_index: int = 2 # This is Layer3 in the Inspector
    _bitmask = _bitmask | (1 << _bit_index) # Enable bit on bitmask.
    parameters.collision_mask = _bitmask # Set bitmask for the ray collision

    var exclude: Array[RID] = [] # Array that holds RIDs of already hit colliders
    var direct_space: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state

    var max_collected_colliders: int = 5
    var collected_colliders: Array = []

    for i in range(max_collected_colliders):
        parameters.exclude = exclude # Update parameters with already hit colliders.
        var result = direct_space.intersect_ray(parameters)
        if result:
            exclude.push_back(result.rid) # Exclude hit collider for next ray.
            collected_colliders.push_back(result)

    print(collected_colliders)