https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html

I recently upgraded my Godot projects to Godot 4 Beta 16 and have been going through the changes fine by checking out the articles and documentation, for the life of me though I can't work out how to get a simple raycast done to the terrain mouse position like I used to because the syntax seems to have changed so much. The example at the bottom seems straightforward but it only seems to have my object following the mouse at a fixed range rather than along the terrain as I want which I used get_collision_point() for.

Presumably PhysicsDirectSpaceState3D is related and intersect_point? That doesn't seem right though, this is in older versions too, I've gotten lost lol.

https://docs.godotengine.org/en/latest/classes/class_physicsdirectspacestate3d.html#class-physicsdirectspacestate3d

  • Megalomaniak replied to this.
  • Fixed it! I'd say that's done now because that's way smoother, instead of having the code halt entirely I used the older code I found out about by accident and I'm having the 3D cursor move to the raycast based on it's range rather than what it collides in. That way as far as the player is concerned the cursor is still technically 'working' and you don't have to worry about any silliness with collision.

    Thanks for your help guys, hopefully this helps out other people.

    	var spaceState = get_world_3d().direct_space_state
    	var mousePosition = get_viewport().get_mouse_position()
    	var raycastOrigin = playerCamera.project_ray_origin(mousePosition)
    	var raycastTarget = raycastOrigin + playerCamera.project_ray_normal(mousePosition) * rayLength
    	var physicsRaycastQuery = PhysicsRayQueryParameters3D.create(raycastOrigin, raycastTarget)
    	var raycastResult = spaceState.intersect_ray(physicsRaycastQuery)
    	
    	if raycastResult.is_empty():
    		global_transform.origin = raycastTarget
    	else:
    		global_transform.origin = raycastResult["position"]

    That's what I'd use normally, have I just overcomplicated this and is it in fact still the same to get the collision point? I think what's throwing me off is having to raycast the mouse position onto the terrain.

    I was asking because of what you said previously:

    Lethn The example at the bottom seems straightforward but it only seems to have my object following the mouse at a fixed range rather than along the terrain as I want which I used get_collision_point() for.

    indicating that you were using Raycast class previously if I understood correct, but in godot 4 it was split into Raycast2D and Raycast3D so I figured you might have gone looking for get_collision_point() from PhysicsDirectSpaceState3D instead of from Raycast3D.

      Megalomaniak That explains why I've been so weirded out by the changes, what I'm looking for is to do a raycast that's end point follows the mouse position and then puts the result onto terrain in 3D co-ordinates, in other words a 3D mouse cursor rather than a 2D one. The navmesh stuff floored me a bit as well because they had completely changed the names of everything even though the logic was still mostly the same lol.

      Does this mean then that intersect_point() is now it's own whole thing? I haven't found any examples on how to use it.

      Oohohooo I'm finding this code fiddly, this is what I'm messing with so far, obviously wrong.

      extends Node3D
      
      @onready var playerCameraGroup = get_tree().get_nodes_in_group("PlayerCamera")
      @onready var playerCamera = playerCameraGroup[0]
      
      var rayLength = 50
      
      func _physics_process(_delta):
      	var mousePosition = get_viewport().get_mouse_position()
      	var raycastOrigin = playerCamera.project_ray_origin(mousePosition)
      	var raycastTarget = raycastOrigin + playerCamera.project_ray_normal(mousePosition) * rayLength
      	
      	var spaceState = get_world_3d().direct_space_state
      	var physicsState3D = PhysicsRayQueryParameters3D.create(raycastOrigin, raycastTarget)
      	var stateResult = spaceState.intersect_ray(physicsState3D)
      	
      	global_transform.origin = stateResult

      I kind of get what the query parameters are for now, have trouble putting it altogether though, was kind of winging it from the 2D example my code is such a mess lol. I preferred the old syntax.

      If I understand your issue as defined by OP correct

      Lethn The example at the bottom seems straightforward but it only seems to have my object following the mouse at a fixed range rather than along the terrain as I want which I used get_collision_point() for.

      Perhaps just try increasing the rayLength value? Also, shouldn't it maybe be

      	global_transform.origin = stateResult[position]

      The physicsState3D will hold a PhysicsRayQueryParameters3D which when calling that intersect_ray(physicsState3D) will return a dictionary.

      The problem is if I do it with the increased length like you normally would say 1000, what happens is it just disappears underneath the terrain entirely. Trying to use the query that way resulted in an a error.

      Invalid get index '(-301.634, 48.9569, -287.168)' (on base: 'Dictionary').

      This is frustrating when the syntax changes this much because what I'm trying to do is a fairly standard mechanic LOL.

        interesting, but is that '(-301.634, 48.9569, -287.168)' likely the right positional value? As in what you would expect.

        Lethn The problem is if I do it with the increased length like you normally would say 1000, what happens is it just disappears underneath the terrain entirely.

        What if you remove the rayLength altogether. Or maybe the second/duplicate project_ray_normal() and then increase the rayLength(to say 100).

        	var raycastOrigin = playerCamera.project_ray_origin(mousePosition)
        	var raycastTarget = raycastOrigin + playerCamera.project_ray_normal(mousePosition) * rayLength

        it's currently adding the values of the playerCamera.project_ray_normal(mousePosition) twice.

        Lethn This is frustrating when the syntax changes this much because what I'm trying to do is a fairly standard mechanic LOL.

        Care to maybe share the original code as you had it in godot 3?

          Megalomaniak Unfortunately I didn't have the sense to make a copy because I thought this wouldn't be a problem to get through LOL what I can do is double check my bookmarks and poke around the internet because I'm very sure that it was based off the code someone may have posted up. It's a very similar thing to when you want to place buildings as an example. You get the mouse position from the camera, do maths with raycast to get 3D co-ordinates, check if the raycast following those co-ordinates on the collision point returned and then have the node follow the result.

          It's extremely frustrating because I even uploaded a video of the previous code working LOL, when we get this solved I'm going to make notes.

          https://odysee.com/@Lethn:3/Building-selection-in-for-the-scaffold:a?r=9uVBCUZNktJFSny4AurVAhURKecvDwFD

          So close! Went and found some base building code because it's practically the same, worked for about five seconds then complained when I moved the mouse.

          	var spaceState = get_world_3d().direct_space_state
          	var mousePosition = get_viewport().get_mouse_position()
          	var raycastOrigin = playerCamera.project_ray_origin(mousePosition)
          	var raycastTarget = raycastOrigin + playerCamera.project_ray_normal(mousePosition) * rayLength
          	var physicsRaycastQuery = PhysicsRayQueryParameters3D.create(raycastOrigin, raycastTarget)
          	var raycastResult = spaceState.intersect_ray(physicsRaycastQuery)
          	
          	global_transform.origin = raycastResult["position"]

          Invalid get index 'position' (on base: 'Dictionary').

          Its probably failing when the raycast isn't hitting anything (raycastResult is an empty Dictionary), so you'd need to check for that or use Dictionary.get

          It is beyond creepy how I automatically solve code in my sleep but here we go the autocomplete gave me a hint at the end for the exact syntax. I feel like this is definitely a bit of a hacky solution to the problem, I'm sure there are better ways to do this more smoothly, I'm not sure I like these changes lol, thanks for the hints @spacecloud they definitely helped.

          	var spaceState = get_world_3d().direct_space_state
          	var mousePosition = get_viewport().get_mouse_position()
          	var raycastOrigin = playerCamera.project_ray_origin(mousePosition)
          	var raycastTarget = raycastOrigin + playerCamera.project_ray_normal(mousePosition) * rayLength
          	var physicsRaycastQuery = PhysicsRayQueryParameters3D.create(raycastOrigin, raycastTarget)
          	var raycastResult = spaceState.intersect_ray(physicsRaycastQuery)
          	
          	if raycastResult.is_empty():
          		return
          	else:
          		global_transform.origin = raycastResult["position"]

          The issue now is when my mouse goes above the terrain it stops which is pretty jarring, but I think this can be solved with a sphere collider or something as a work around. A common trick for shooters so even if you're firing into an empty space the game still works smoothly. Hopefully this code should at least get people on the right track if they're struggling with the new raycast system like I have been, at least I got rid of any major errors.

          Or maybe actually a few box colliders to make the raycasts think you're in a room? That seems more practical.

            Fixed it! I'd say that's done now because that's way smoother, instead of having the code halt entirely I used the older code I found out about by accident and I'm having the 3D cursor move to the raycast based on it's range rather than what it collides in. That way as far as the player is concerned the cursor is still technically 'working' and you don't have to worry about any silliness with collision.

            Thanks for your help guys, hopefully this helps out other people.

            	var spaceState = get_world_3d().direct_space_state
            	var mousePosition = get_viewport().get_mouse_position()
            	var raycastOrigin = playerCamera.project_ray_origin(mousePosition)
            	var raycastTarget = raycastOrigin + playerCamera.project_ray_normal(mousePosition) * rayLength
            	var physicsRaycastQuery = PhysicsRayQueryParameters3D.create(raycastOrigin, raycastTarget)
            	var raycastResult = spaceState.intersect_ray(physicsRaycastQuery)
            	
            	if raycastResult.is_empty():
            		global_transform.origin = raycastTarget
            	else:
            		global_transform.origin = raycastResult["position"]

            Just an fyi there definitely needs to be a bit of a writeup done on the latest documentaton on this if anyone has the time because this was annoying to deal with lol. The initial mouse click example I saw in the documentation was too basic.

            spacecloud you need quotes stateResult["position"]

            Good catch, totally my bad. Forgot to add them since I wasn't trying code out in editor or anything, just looking through the docs and then switch tabs back to here typing it manually in the forum post editbox.

            Lethn It is beyond creepy how I automatically solve code in my sleep

            Yeah it's always good to sleep on a problem.