Help to make the character rotate normally relative to the sphere

Godot Version

4

Question

Hi! I am working on a game in which there is a mechanic according to which the character must move around a three-dimensional sphere, following the mouse and rotating so that he is 90 degrees relative to the sphere. The first time, I implemented it as follows:

extends CharacterBody3D

var target_position = null
var SPEED = 1
var pol = 0.9
var radius = 0.75

func _physics_process(delta: float) -> void:
	rotation.y = 0
	if $RayCast1.is_colliding():
		rotate_x(rotation.x - tan(pol) * delta)
	if $RayCast2.is_colliding():
		rotate_x(rotation.x + tan(pol) * delta)
	if $RayCast3.is_colliding():
		rotate_z(rotation.z - tan(pol) * delta)
	if $RayCast4.is_colliding():
		rotate_z(rotation.z + tan(pol) * delta)

	if $RayCast5.is_colliding():
		rotate_x(rotation.x - tan(pol) * delta)
	if $RayCast6.is_colliding():
		rotate_x(rotation.x + tan(pol) * delta)
	if $RayCast7.is_colliding():
		rotate_z(rotation.z - tan(pol) * delta)
	if $RayCast8.is_colliding():
		rotate_z(rotation.z + tan(pol) * delta)
	
	
	if Input.is_action_pressed('left_click'): # Mouse position
		var viewport := get_viewport()
		var mouse_position := viewport.get_mouse_position()
		var camera := viewport.get_camera_3d()
		var origin := camera.project_ray_origin(mouse_position)
		var direction := camera.project_ray_normal(mouse_position)
		var ray_length := camera.far
		var end := origin + direction * ray_length
		var space_state := get_world_3d().direct_space_state
		var query := PhysicsRayQueryParameters3D.create(origin, end)
		var result := space_state.intersect_ray(query)

		target_position = result["position"]

	if target_position != null: # check
		var direction = target_position - global_position # Use global position
		direction = direction.normalized()

		# Rotate the character towards the target using look_at
		var char = $Warrior
		char.look_at(target_position, Vector3.UP)
		char.rotation.x = 0
		char.rotation.z = 0
		char.rotation.y = 0

		velocity = direction * SPEED
		#velocity.y = 0

		if position.distance_to(target_position) <= 0.1:
			target_position = null
			velocity.y = 0
			velocity.x = 0
			velocity.z = 0
	else:
		target_position = null
		velocity.y = 0
		velocity.x = 0
		velocity.z = 0

	move_and_slide()

But due to the RayCast3D, the character wobbled terribly. I couldn’t figure out how to implement it properly. After asking an AI, I got this:

extends CharacterBody3D

var target_position = null
var SPEED = 1
var radius = 0.55

func _physics_process(delta: float) -> void:
	#print(rotation)xyz
	if Input.is_action_pressed('left_click'):
		var viewport := get_viewport()
		var mouse_position := viewport.get_mouse_position()
		var camera := viewport.get_camera_3d()
		var origin := camera.project_ray_origin(mouse_position)
		var direction := camera.project_ray_normal(mouse_position)
		var ray_length := camera.far
		var end := origin + direction * ray_length
		var space_state := get_world_3d().direct_space_state
		var query := PhysicsRayQueryParameters3D.create(origin, end)
		var result := space_state.intersect_ray(query)

		target_position = result["position"]

	if target_position != null:
		var direction = target_position - global_position
		direction = direction.normalized()

		# Calculate the new position on the sphere
		var new_position = global_position + direction * SPEED * delta
		new_position = new_position.normalized() * radius

		# Update the character's position and rotation
		global_position = new_position
		look_at(global_position + direction, Vector3.UP)
		rotation.x += deg_to_rad(90)

		if global_position.distance_to(target_position) <= 0.01:
			target_position = null
	else:
		target_position = null

	move_and_slide()

It works almost as I wanted. I’d like you to explain how this works (not the click detection, but the modified movement). Also, there’s an issue in the code where, when the character moves toward its target, it ends up lying horizontally relative to the sphere. Could you suggest how to fix this?

Explaining the machinations of a neural network is a fool’s errand. Couldn’t you just ask it to explain?

Back to the topic: can you elaborate on how a character should follow a mouse cursor? (I can imagine it easily in 2d, but not in 3d)

1 Like

When we get the viewport, we encounter a solid surface, we get the mouse coordinates, but when the click passes through the void, an error occurs, and this is followed by a check for “mouse_left”. Then we subtract the mouse position from the global coordinates, perform mathematical calculations, and use velocity and look_at() to move to the desired point. Then, when we start, it checks if we have reached the goal, and if we have stopped, we reset the mouse click position. Now I’ve described how it works in the upper example code, but I still don’t understand what’s going on in the lower example. Here is a link to my question on this topic —> https://forum .godotengine.org/t/i-do-not-know-how-to-make-a-character-move-like-in-diablo-4-or-torchlight-2/58336

Hello everyone! I found a way to solve my problem. I should have done this: look_at((global_position + direction) * Vector3(0,0,0), Vector3.UP)

I found the answer using the selection method.

Hi there. My game also features many entities moving across a spherical map. I mostly exploit “look_at()” to make them look at the center of the planet, and that does most of the lifting. One specific issue I see with the AI code is that it uses Vector3.UP. In most instances when I’ve wanted to move around my sphere, I’ve had to calculate a custom up that is the vector between my entity and my sphere. I would be happy to call on Discord or something to show you how it all works. I’m @BluntBSE

I’ll just add that interpolating across a 3d surface is not the most trivial of problems, as it might seem. The approach largely depends on what your end goals are (will there be physics, will the character be able to jump/fly/dig, will it be a perfect surface, etc).

I’ll direct you to a thread that talks extensively about movement/interpolation across a sphere with physics, like a planet:

1 Like

Thank you