The Godot Q&A is currently undergoing maintenance!

Your ability to ask and answer questions is temporarily disabled. You can browse existing threads in read-only mode.

We are working on bringing this community platform back to its full functionality, stay tuned for updates.

godotengine.org | Twitter

0 votes

I have a setup where if the gun is active, update_current_target() is called in physics process.
What it should do is first get all bodies in RangeDetector which is an Area2D and then filter it to get only those in the specified group.
Then it uses AimCheck, which is a Raycast2D, to check if there is a clear line between the gun and the targets.
Then it gets the nearest target and sets it as the gun's primary target.

I noticed that it doesn't work as expected with some test objects i set to pass by.
Direction and Speed
The east target touches first and the raycast targets it immediately. But get_collider() first returns [Object:null], then correctly sets it as the target in a split second then suddenly ignores it as though it's no longer a valid target.
The north target is tracked correctly from when it enters to when it exits. As it nears the bottom edge the south target becomes the nearest but it's ignored even after the north target leaves the area. Then it tracks the south target for less than a second and then stops.
It ignores the west target as expected since a static body is blocking it.

var current_target: Node

func update_current_target():
    var target_node: Node
    var possible_nodes: = []
    var clear_nodes: = []
    for item in RangeDetector.get_overlapping_bodies():
        if item.is_in_group(target_group):
            possible_nodes.append(item)
    for item in possible_nodes:
        if has_clear_shot(item):
            clear_nodes.append(item)
    if clear_nodes.size() > 0:
        target_node = clear_nodes[0]
        for body in clear_nodes:
            if (body.global_position - self.global_position).length() < (target_node.global_position - self.global_position).length():
                target_node = body
    else:
        target_node = null
    current_target = target_node

func has_clear_shot(target: Node):
    AimCheck.set_cast_to(target.global_position - self.global_position)
    return AimCheck.get_collider() == target

I spent a lot of time and i still don't know how to fix it. My best guess is that get_collider() returns the wrong node even after setting its cast vector but that doesn't make sense.

in Engine by (26 points)
if (body.global_position - self.global_position).length() < (target_node.global_position - self.global_position).length():
                target_node = body

does it enter in this statement?

also (likely it doesnt even matter) but is the raycast set to exclude parent?

It's done in physics process so it's more like processing a snapshot of all the targets.
Yes the raycast excludes the parent although the parent is a Node2D and raycast only seems to work on bodies and/or areas. And even then the first thing I do is filter objects in the group.

can you share the project?

sorry but the project is not complete (it's even missing the project.godot file) and if use the nodes you provided it returns a bunch of errors because there are missing nodes and script.
If you dont feel like sharing the entire project, try replicate the issue on a smaller but working project, and share the entire folder

The paths were pointing to the wrong directory. I've fixed all the dependencies and put it in a proper project. I haven't used the gun elsewhere so it will work the same with just the relevant directory.
https://www.dropbox.com/sh/iixri0jaymkap5e/AADwjPEwifqcRuULhkRrQFj1a?dl=0

1 Answer

+1 vote
Best answer

the clear_shot function you are using to check if the target is clear is not working, because the the physic world state not being updated between one ray cast and the next one (if you notice, the clear_node array is always size 1, because it works only once a frame).
Use AimCheck.force_raycast_update() immediatly after set_cast_to(),

Another solution (less nodes and always updated): you can replace with an instance ray
https://docs.godotengine.org/en/stable/tutorials/physics/ray-casting.html

func clear_shot(target: Node):
    var space_state = get_world_2d().direct_space_state
    var result = space_state.intersect_ray(global_position, target.global_position)
    return result.collider == target
by (1,514 points)
selected by

It worked perfectly. Thank you for your help!

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.