Get distance from RayCast3D hitpoint to a Node3D from the viewpoint of the Camera3D

Godot Version

4.4

Question

Think about a mechanic like in Pokemon Snap where you take a photo of a monster. When taking the photo, I want to know the offset from the center of the monster to measure the acuratcy of the player.
The monster most times , but not exclusively, has a spherical collisionshape. The center of the monster is therefore determined by a seperate “Focus” Node3D which is just there for positional means.

Think of this structure:
Photo ← script is here
Monster

  • Focus
  • Area3D
    – CollisionShape3D
    Camera
  • RayCast3D

The resulting view is like in this screenshot.

So when I “take a photo” the ray may be on the outer edge of the sphere when viewed from the camera, far away from the monster center. How could I rate the accuracy of the player?

I can’t wrap my head around it. Something like get the positions in local coordinates as from the ray?

All I got so far is this:

var distance: float = 0
var ray_collision_object: Area3D = snapshot_ray.get_collider()
var ray_collision_point: Vector3 = snapshot_ray.get_collision_point()
var monster_center: Vector3 = ray_collision_object.get_node("../Focus").position
print(ray_collision_point.distance_to( monster_center )) // won't give correct results, as the hit point could be far in front of the Focus

I’m happy to give more details if needed :slight_smile:

I think it’s easiest to do in screenspace instead of worldspace. Unproject the targets position and calculate the distance to viewport center:

func dist_to_view_center(pos):
	var viewport = get_viewport()
	var cam = viewport.get_camera_3d()
	var view_center = viewport.get_visible_rect().size / 2.0
	var screenpos = cam.unproject_position(pos)
	var dist_to_center = screenpos.distance_to(view_center)

	print(dist_to_center)
	
	return dist_to_center

Pass the targets global_position to that function and it returns the distance in pixels. There’s just one problem: viewport resolution would affect gameplay. So you might want to divide screenpos by resolution to make it the coordinates work same on everyones machine:

func dist_to_view_center_v2(pos):
	var viewport = get_viewport()
	var cam = viewport.get_camera_3d()
	var view_size = viewport.get_visible_rect().size
	var screenpos = cam.unproject_position(pos) / view_size
	var view_center = Vector2(0.5, 0.5)
	var dist_to_center = screenpos.distance_to(view_center)

	print(dist_to_center)
	
	return dist_to_center

Like this the coordinates go from 0.0 to 1.0, instead of whatever the resolution is. Center of the viewport is (0.5, 0.5).

If distance is bigger than 0.5, target point is out of view.

Use it something like this:

var acc = 1.0 - dist_to_view_center_v2(target.global_position)
print("Accuracy: ", acc * 100, "%")
1 Like

Thanks. I didn’t know about the unproject_position function. It is so simple if you know you can just use the “screenshot” coordinates for calculations. Your suggestion about the followup percentage was also a good prediction :grinning: