Sorry for the delay. I integrated your code with my old player script and am happy to say it works wonderfully! I managed to simplify it a bit so this is the only transform required for the base math:
global_basis.y = global_position.normalized()
global_basis.x = global_basis.y.cross(global_basis.z)
global_basis = global_basis.orthonormalized()
Here's the full player script, it contains more features including distance scaling and inverted gravity for inside-out planets or rings:
extends CharacterBody3D
@export var Sensitivity_X = 0.01
@export var Sensitivity_Y = 0.01
@export var Minimum_Y_Look = -90
@export var Maximum_Y_Look = 90
@export var Accelaration = 100
@export var Decelaration = 25
@export var Air_Accelaration = 50
@export var Air_Decelaration = 5
@export var Jump_Speed = 500
@export var Jump_Jetpack = true
@export var Gravity = 2500
@export var Gravity_Sphere = true
func _ready():
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _physics_process(delta):
var input_dir = Input.get_vector(&"ui_left", &"ui_right", &"ui_up", &"ui_down")
var accel = Accelaration if is_on_floor() else Air_Accelaration
var decel = Decelaration if is_on_floor() else Air_Decelaration
# Apply velocity: Gravity, jump, movement, friction
if not is_on_floor():
var dist = 1 / (1 + global_position.distance_to(Vector3i(0, 0, 0)))
velocity -= global_basis.y * abs(Gravity) * dist * delta
if (Jump_Jetpack or is_on_floor()) and Input.is_action_pressed(&"ui_accept"):
velocity += global_basis.y * abs(Jump_Speed) * delta
velocity += (global_basis.x * input_dir.x + global_basis.z * input_dir.y) * accel * delta
velocity /= 1 + decel * delta
move_and_slide()
# Apply sphere orientation, overrides up_direction for move_and_slide to work correctly
if Gravity_Sphere:
var dir = +1 if Gravity >= 0 else -1
global_basis.y = global_position.normalized() * dir
global_basis.x = global_basis.y.cross(global_basis.z)
global_basis = global_basis.orthonormalized()
up_direction = global_basis.y
func _input(event):
if event is InputEventMouseMotion:
rotate_object_local(Vector3.DOWN, event.relative.x * Sensitivity_X)
$PlayerCamera.rotate_x(-event.relative.y * Sensitivity_Y)
$PlayerCamera.rotation.x = min(deg_to_rad(Maximum_Y_Look), max(deg_to_rad(Minimum_Y_Look), $PlayerCamera.rotation.x))
There is but one issue left, and it would be amazing if anyone could give me a hand with it as well. As can be seen in the image, the atmosphere doesn't rotate with the player when using a PhysicalSkyMaterial
, which causes the horizon to tilt and doesn't look realistic from behind: The horizon should always be down from the perspective of the player, the sun should shine from the same direction but moved to the new position on the rotated sky. If I create a script for my WorldEnvironment
node to rotate it and its DirectionalLight3D
child, what should it contain to get a proper results? It should probably have an offset for specifying the time of day, I could plug the time into it to get a daytime cycle and run it at a different phase for the moon.