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?