0 votes

CURRENT BEST SOLUTION:
Make a RayCast node the child of the camera and set its position and rotation to (0,0,0). Check the "enabled" and "exclude parent" boxes and make sure to use "cast to" to set the length of the raycast rather than using "scale." Make sure to use forceraycastupdate() in your method, or else the raycast can return null references. I had one other point of confusion, which was that my crosshair was actually slightly off-center, which I had to fix. The following code has no issues:

func use_weapon():
$RayCast.force_raycast_update()

if !$RayCast.is_colliding():
    return

var collider = $RayCast.get_collider()
if collider.has_method("damage"):
    collider.damage(10)

There's only one problem left, which is that the raycast collides with enemy vision cones. I'll have to set the collision masks to fix that I think.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
QUESTION:
tl;dr: Does anyone know how to do FPS-style hitscan shooting correctly in Godot? Not just a workaround or a hack, but the most sensible and efficient way? Or is there a good tutorial or code example that shows how to do hitscan shooting in an FPS?

I want to cast a ray from the center of my camera to a point some distance away (say, 250 units from the camera, in the direction the camera is looking), preferably with the ability to slightly angle it randomly for bullet scatter. So far I've tried two different methods but they're both pretty broken the way I'm trying it:

var space_state = get_world().direct_space_state
var aiming = camera.global_transform.basis
var ray = space_state.intersect_ray(camera.global_transform.origin, aiming.xform(Vector3(0,0,-100)), [player, self])

And then getting the collisions, checking methods, etc. I'm using -100 for the z axis because it's flipped for some reason. It detects things maybe 5 or ten units in front of me, but definitely not 100 units. And I'm also trying the raycast node:

if !$RayCast.is_colliding():
        return
    var collider = $RayCast.get_collider()
    var ref = weakref(collider)
    if (!ref.get_ref()):
        return
    if ref.get_ref().has_method("damage"):
        collider.damage(10)

This has a couple problems. One is that it also doesn't detect things very far, even though I've made its length 300, and the other is that it keeps trying to call methods on null references, and even with the weakref checking, it gets a nil reference error (after correctly killing the enemy, if I hit the fire button too soon).
Personally, I'm not a fan of using the raycast node rather than the space state intersect_ray, since it seems inelegant anyhow.

in Engine by (15 points)
edited by

1 Answer

+1 vote
Best answer

Did you know that there is now an FPS tutorial on Godot docs? One of its parts talk about making bullets with rayCasts(hitscan). Hope you find what your looking for.

by (3,873 points)
selected by

I think it's in part two somewhere.

This example solves one problem I've been having, which is inconsistent hit detection.

    $RayCast.force_raycast_update()

Does the job.

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 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 webmaster@godotengine.org with your username.