I am making a 3D top down view game that requires aiming with the mouse. It rotates at wierd angles. After adjusting the transforms, I managed to make the nodes in the $Rotate spatial node centered which made it look slightly better but not like the tutorial I was watching.

From my understanding, I needed to swap out '$Rotate.look_at(Vector3(pos.x, pos.y, pos.z), Vector3(0, 1, 0))' with '$Rotate.look_at(Vector3(pos.x, translation.y, pos.z), Vector3(0, 1, 0))' or '$Rotate.look_at(Vector3(pos.x, $Rotate.translation.y, pos.z), Vector3(0, 1, 0))'

And the code below still didn't work:

# Get current physics state
var space_state = get_world().direct_space_state

# Get current mouse position in the viewport
var mouse_position = get_viewport().get_mouse_position()

rayOrigin = Global.Camera.project_ray_origin(mouse_position) # Set the ray origin
	
rayEnd = rayOrigin + Global.Camera.project_ray_normal(mouse_position) * 2000 # Set the ray end point
	
var intersection = space_state.intersect_ray(rayOrigin, rayEnd) # Get the ray hit
	
if not intersection.empty(): # If there is a proper ray hit get its position and rotate towards it
	var pos = intersection.position
	$Rotate.look_at(Vector3(pos.x, pos.y, pos.z), Vector3(0, 1, 0))

I also had got this code below to work in the past and I am struggling to see why this doesn't work anymore and neither does the code above. I do not think the code below does anything much different. All it seems to do is rely on another node to get its rotation which now I realize may be over-convoluted compared to the code above which I was hoping would work.

# Create a horizontal plane, and find a point where the ray intersects with it
	var player_pos = global_transform.origin
	var dropPlane  = Plane(Vector3(0, 1, 0), player_pos.y)
	# Project a ray from camera, from where the mouse cursor is in 2D viewport
	var ray_length = 1000
	var mouse_pos = get_viewport().get_mouse_position()
	var from = Global.Camera.project_ray_origin(mouse_pos)
	var to = from + Global.Camera.project_ray_normal(mouse_pos) * ray_length
	var cursor_pos = dropPlane.intersects_ray(from,to)
	
	if cursor_pos != null:
			# Rotate mesh to look at cursor
			PointFromNode.look_at(cursor_pos, Vector3.DOWN)
			var _mesh_pos = MeshInstanceNode.transform.origin
			PointFromNode.transform.origin = Vector3(_mesh_pos.x, PointFromNode.transform.origin.y, _mesh_pos.z)
			
			# Signal emitted when custom rotations are needed to be applied
#			emit_signal("apply_rotation", self)
			
			# Get mesh node to only rotate by y axis
			MeshInstanceNode.rotation.y = PointFromNode.rotation.y
			#var rotation = MeshInstanceNode.transform.basis

Best regards, Oliver

The first code you posted is the correct way to do it. I don't see any obvious errors, so most of the code is probably fine. The one line that looks strange is this:

if not intersection.empty():

It should be something like this (this is from my project, you might have to alter it slightly):

if result.has("position") and result.has("collider"):
    if result.get("collider").name == "Cube":
        world_pos = result.get("collider").translation

I added a cube MeshInstance to the scene (called it 'Ground').

Created a trimesh static body (and renamed that to 'Ground').

Edited the code to :

func _physics_process(delta):
	
	# Get current physics state
	var space_state = get_world().direct_space_state
	
	# Get current mouse position in the viewport
	var mouse_position = get_viewport().get_mouse_position()
	
	rayOrigin = Global.Camera.project_ray_origin(mouse_position) # Set the ray origin
	
	rayEnd = rayOrigin + Global.Camera.project_ray_normal(mouse_position) * 2000 # Set the ray end point
	
	var intersection = space_state.intersect_ray(rayOrigin, rayEnd) # Get the ray hit
	
	if intersection.has("position") and intersection.has("collider"):
		if intersection.get("collider").name == "Ground":
			var pos = intersection.get("collider").translation
			look_at(Vector3(pos.x, translation.y, pos.z), Vector3(0, 1, 0))

Result is hard to describe and I noticed something strange. That when trying to test this out, the camera still rotates even though I added 'Global.Camera.set_as_toplevel(true)' because the camera is a child of the player node as it moves with it.

When I changed the code to get rotation node to 'look_at'. '$Rotate.look_at(Vector3(pos.x, translation.y, pos.z), Vector3(0, 1, 0))', the camera angle didn't look strange but the rotation looks a bit locked and slightly rotates when I move so I think the mouse has a slight influence on the rotation of it but its just completely wrong.

My understanding of this setup, is that it is meant to rotate the mesh and not the whole kinematicbody or collisionshape of the body.

Can you take a screenshot of your node tree and also let me know what object the script is attached to?

That node setup looks really strange to me. There is way too much stuff in the KinematicBody. Usually you only want a collision shape and the 2D/3D graphics (the player or enemy) and that's it. The Camera and gimbal should be separate objects on the same level as the player (or in your case in the main scene, not part of the player scene).

This is a common mistake and causes lots of problems later on, especially with things like camera smoothing or hit detection / picking. It's best to design your games and think of them like a virtual movie set. So you have the player (an actor) the camera (which would be the camera person or director) the scene or level (the set) and they all exist in the same space on the same level, like in real life.

@cybereality said: That node setup looks really strange to me. There is way too much stuff in the KinematicBody. Usually you only want a collision shape and the 2D/3D graphics (the player or enemy) and that's it. The Camera and gimbal should be separate objects on the same level as the player (or in your case in the main scene, not part of the player scene).

This is a common mistake and causes lots of problems later on, especially with things like camera smoothing or hit detection / picking. It's best to design your games and think of them like a virtual movie set. So you have the player (an actor) the camera (which would be the camera person or director) the scene or level (the set) and they all exist in the same space on the same level, like in real life.

I will be re-thinking how this node is setup. The scene structure is designed is such a way that it contains components that the Player is using and for any components that need to be outside in the scene structure, I move them around. I am watching some tutorials which should help me out.

9 months later