How to align 3D player/character with surface?

Godot Version

Godot 4.6

Question

Heya, title explains everything. I wanna align the player with the floor he’s walking on while not affecting movement direction. I tried looking up online but most posts on these forums regarding this issue always just have an old Godot 3.0 tutorial as the solution, which is not what I want. Please help me with this.

Movement script:

extends CharacterBody3D
#-@onready-----------------------------@onready-----------------------------------------------
@onready var Character := $Character
@onready var Camera := $SpringArm3D/Camera3D
var Current_Speed := 0.0
var Accel_Rate := (Constants.Max_Speed - Constants.Min_Speed) / Constants.Accel_Time
var Speed_Stop_Timer := 0.0
var Jump_HoldTime := 0.0
#-------------------------------------------------------------------------------
func _physics_process(delta: float) → void:
if not is_on_floor():
if Input.is_action_pressed(“Jump”) and Jump_HoldTime > 0.0:
velocity.y -= Constants.Gravity * 0.5 * delta
Jump_HoldTime -= delta
else:
velocity.y -= Constants.Gravity * delta
else:
velocity.y = -0.1
#-------------------------------------------------------------------------------
if Input.is_action_pressed(“Jump”) and is_on_floor():
Jump_HoldTime = Constants.Max_JumpHoldTime
#-------------------------------------------------------------------------------
velocity.y = Constants.Jump_Velocity
#-------------------------------------------------------------------------------
var Input_Direction = Input.get_vector(“MoveLeft”, “MoveRight”, “MoveBackward”, “MoveForward”)
#-------------------------------------------------------------------------------
var Camera_Basis = Camera.global_basis
var Forward = -Camera_Basis.z
var Right = Camera_Basis.x
#-------------------------------------------------------------------------------
Forward.y = 0
Right.y = 0
#-------------------------------------------------------------------------------
Forward = Forward.normalized()
Right = Right.normalized()
#-------------------------------------------------------------------------------
var Direction = (Forward * Input_Direction.y + Right * Input_Direction.x).normalized()
#-------------------------------------------------------------------------------
if Direction.length() > 0:
var Target_Angle = atan2(Direction.x, Direction.z)
var Angle_Diff = abs(wrapf(Target_Angle - Character.rotation.y, -PI, PI))
#-------------------------------------------------------------------------------
if Angle_Diff > deg_to_rad(150):
Current_Speed *= 0.625
#-------------------------------------------------------------------------------
var Turn_Speed = lerp(8.0, 20.0, Angle_Diff / PI)
#-------------------------------------------------------------------------------
if Input_Direction.y > 0:
Turn_Speed *= 1.5
#-------------------------------------------------------------------------------
Character.rotation.y = lerp_angle(Character.rotation.y, Target_Angle, Turn_Speed * delta)
#-------------------------------------------------------------------------------
if Direction.length() > 0:
Speed_Stop_Timer = 0.0
#-------------------------------------------------------------------------------
Current_Speed += Accel_Rate * delta
Current_Speed = clamp(Current_Speed, Constants.Min_Speed, Constants.Max_Speed)
#-------------------------------------------------------------------------------
velocity.x = Direction.x * Current_Speed
velocity.z = Direction.z * Current_Speed
else:
Speed_Stop_Timer += delta
#-------------------------------------------------------------------------------
var Stop_Rate = Accel_Rate * 14.0
var Horizontal_Velocity = Vector3(velocity.x, 0, velocity.z)
#-------------------------------------------------------------------------------
Horizontal_Velocity = Horizontal_Velocity.move_toward(Vector3.ZERO, Stop_Rate * delta)
#-------------------------------------------------------------------------------
velocity.x = Horizontal_Velocity.x
velocity.z = Horizontal_Velocity.z
#-------------------------------------------------------------------------------
if Speed_Stop_Timer >= Constants.Stop_Speed_Reset_Time:
Current_Speed = 0.0
#-------------------------------------------------------------------------------
move_and_slide()

I’m pretty sure that the problem is in here, but I cannot tell you for sure without properly formatted code. Do this:

```gd
# Copy and paste code from your editor here.
# Not the code you already posted,
# that has lost its indentation.
```

It’ll be formatted like this:

# Copy and paste code from your editor here.
# Not the code you already posted,
# that has lost its indentation.

oh alright, here

```gd
extends CharacterBody3D
#@onready@onready----------------------------@onready@onready-----------------------------------------------@onready@onready var Character := $Character@onready@onready var Camera := $SpringArm3D/Camera3D
var Current_Speed := 0.0
var Accel_Rate := (Constants.Max_Speed - Constants.Min_Speed) / Constants.Accel_Time
var Speed_Stop_Timer := 0.0
var Jump_HoldTime := 0.0
#-------------------------------------------------------------------------------
func _physics_process(delta: float) → void:
if not is_on_floor():
if Input.is_action_pressed(“Jump”) and Jump_HoldTime > 0.0:
velocity.y -= Constants.Gravity * 0.5 * delta
Jump_HoldTime -= delta
else:
velocity.y -= Constants.Gravity * delta
else:
velocity.y = -0.1
#-------------------------------------------------------------------------------
if Input.is_action_pressed(“Jump”) and is_on_floor():
Jump_HoldTime = Constants.Max_JumpHoldTime
#-------------------------------------------------------------------------------
velocity.y = Constants.Jump_Velocity
#-------------------------------------------------------------------------------
var Input_Direction = Input.get_vector(“MoveLeft”, “MoveRight”, “MoveBackward”, “MoveForward”)
#-------------------------------------------------------------------------------
var Camera_Basis = Camera.global_basis
var Forward = -Camera_Basis.z
var Right = Camera_Basis.x
#-------------------------------------------------------------------------------
Forward.y = 0
Right.y = 0
#-------------------------------------------------------------------------------
Forward = Forward.normalized()
Right = Right.normalized()
#-------------------------------------------------------------------------------
var Direction = (Forward * Input_Direction.y + Right * Input_Direction.x).normalized()
#-------------------------------------------------------------------------------
if Direction.length() > 0:
var Target_Angle = atan2(Direction.x, Direction.z)
var Angle_Diff = abs(wrapf(Target_Angle - Character.rotation.y, -PI, PI))
#-------------------------------------------------------------------------------
if Angle_Diff > deg_to_rad(150):
Current_Speed *= 0.625
#-------------------------------------------------------------------------------
var Turn_Speed = lerp(8.0, 20.0, Angle_Diff / PI)
#-------------------------------------------------------------------------------
if Input_Direction.y > 0:
Turn_Speed *= 1.5
#-------------------------------------------------------------------------------
Character.rotation.y = lerp_angle(Character.rotation.y, Target_Angle, Turn_Speed * delta)
#-------------------------------------------------------------------------------
if Direction.length() > 0:
Speed_Stop_Timer = 0.0
#-------------------------------------------------------------------------------
Current_Speed += Accel_Rate * delta
Current_Speed = clamp(Current_Speed, Constants.Min_Speed, Constants.Max_Speed)
#-------------------------------------------------------------------------------
velocity.x = Direction.x * Current_Speed
velocity.z = Direction.z * Current_Speed
else:
Speed_Stop_Timer += delta
#-------------------------------------------------------------------------------
var Stop_Rate = Accel_Rate * 14.0
var Horizontal_Velocity = Vector3(velocity.x, 0, velocity.z)
#-------------------------------------------------------------------------------
Horizontal_Velocity = Horizontal_Velocity.move_toward(Vector3.ZERO, Stop_Rate * delta)
#-------------------------------------------------------------------------------
velocity.x = Horizontal_Velocity.x
velocity.z = Horizontal_Velocity.z
#-------------------------------------------------------------------------------
if Speed_Stop_Timer >= Constants.Stop_Speed_Reset_Time:
Current_Speed = 0.0
#-------------------------------------------------------------------------------
move_and_slide()
```

Why didn’t it work?

That’s very weird. Try pressing Ctrl + E

That just makes it preformatted

Works for me when I quote you, but all the indentation is lost. What device/OS/Browser are you on?

I’m on Firefox

Try Chrome.

```
extends CharacterBody3D
#-@onready-----------------------------@onready-----------------------------------------------
@onready var Character := $Character
@onready var Camera := $SpringArm3D/Camera3D
var Current_Speed := 0.0
var Accel_Rate := (Constants.Max_Speed - Constants.Min_Speed) / Constants.Accel_Time
var Speed_Stop_Timer := 0.0
var Jump_HoldTime := 0.0
#-------------------------------------------------------------------------------
func _physics_process(delta: float) → void:
if not is_on_floor():
if Input.is_action_pressed(“Jump”) and Jump_HoldTime > 0.0:
velocity.y -= Constants.Gravity * 0.5 * delta
Jump_HoldTime -= delta
else:
velocity.y -= Constants.Gravity * delta
else:
velocity.y = -0.1
#-------------------------------------------------------------------------------
if Input.is_action_pressed(“Jump”) and is_on_floor():
Jump_HoldTime = Constants.Max_JumpHoldTime
#-------------------------------------------------------------------------------
velocity.y = Constants.Jump_Velocity
#-------------------------------------------------------------------------------
var Input_Direction = Input.get_vector(“MoveLeft”, “MoveRight”, “MoveBackward”, “MoveForward”)
#-------------------------------------------------------------------------------
var Camera_Basis = Camera.global_basis
var Forward = -Camera_Basis.z
var Right = Camera_Basis.x
#-------------------------------------------------------------------------------
Forward.y = 0
Right.y = 0
#-------------------------------------------------------------------------------
Forward = Forward.normalized()
Right = Right.normalized()
#-------------------------------------------------------------------------------
var Direction = (Forward * Input_Direction.y + Right * Input_Direction.x).normalized()
#-------------------------------------------------------------------------------
if Direction.length() > 0:
var Target_Angle = atan2(Direction.x, Direction.z)
var Angle_Diff = abs(wrapf(Target_Angle - Character.rotation.y, -PI, PI))
#-------------------------------------------------------------------------------
if Angle_Diff > deg_to_rad(150):
Current_Speed *= 0.625
#-------------------------------------------------------------------------------
var Turn_Speed = lerp(8.0, 20.0, Angle_Diff / PI)
#-------------------------------------------------------------------------------
if Input_Direction.y > 0:
Turn_Speed *= 1.5
#-------------------------------------------------------------------------------
Character.rotation.y = lerp_angle(Character.rotation.y, Target_Angle, Turn_Speed * delta)
#-------------------------------------------------------------------------------
if Direction.length() > 0:
Speed_Stop_Timer = 0.0
#-------------------------------------------------------------------------------
if is_on_floor():
Current_Speed += Accel_Rate * delta
else:
Current_Speed += lerp(Current_Speed, 20.0, 0.5 * delta)
Current_Speed = clamp(Current_Speed, Constants.Min_Speed, Constants.Max_Speed)
#-------------------------------------------------------------------------------
velocity.x = Direction.x * Current_Speed
velocity.z = Direction.z * Current_Speed
else:
Speed_Stop_Timer += delta
#-------------------------------------------------------------------------------
var Stop_Rate = Accel_Rate * 14.0
var Horizontal_Velocity = Vector3(velocity.x, 0, velocity.z)
#-------------------------------------------------------------------------------
Horizontal_Velocity = Horizontal_Velocity.move_toward(Vector3.ZERO, Stop_Rate * delta)
#-------------------------------------------------------------------------------
velocity.x = Horizontal_Velocity.x
velocity.z = Horizontal_Velocity.z
#-------------------------------------------------------------------------------
if Speed_Stop_Timer >= Constants.Stop_Speed_Reset_Time:
Current_Speed = 0.0
#-------------------------------------------------------------------------------
move_and_slide()
```

bruh ok, imma just send the code as a mediafire file link

Ok, here’s the code:

movement.gd
extends CharacterBody3D
#-------------------------------------------------------------------------------
@onready var Character := $Character
@onready var Camera := $SpringArm3D/Camera3D
var Current_Speed := 0.0
var Accel_Rate := (Constants.Max_Speed - Constants.Min_Speed) / Constants.Accel_Time
var Speed_Stop_Timer := 0.0
var Jump_HoldTime := 0.0
#-------------------------------------------------------------------------------
func _physics_process(delta: float) -> void:
	if not is_on_floor():
		if Input.is_action_pressed("Jump") and Jump_HoldTime > 0.0:
			velocity.y -= Constants.Gravity * 0.5 * delta
			Jump_HoldTime -= delta
		else:
			velocity.y -= Constants.Gravity * delta
	else:
		velocity.y = -0.1
#-------------------------------------------------------------------------------
	if Input.is_action_pressed("Jump") and is_on_floor():
		Jump_HoldTime = Constants.Max_JumpHoldTime
#-------------------------------------------------------------------------------
		velocity.y = Constants.Jump_Velocity
#-------------------------------------------------------------------------------
	var Input_Direction = Input.get_vector("MoveLeft", "MoveRight", "MoveBackward", "MoveForward")
#-------------------------------------------------------------------------------
	var Camera_Basis = Camera.global_basis
	var Forward = -Camera_Basis.z
	var Right = Camera_Basis.x
#-------------------------------------------------------------------------------
	Forward.y = 0
	Right.y = 0
#-------------------------------------------------------------------------------
	Forward = Forward.normalized()
	Right = Right.normalized()
#-------------------------------------------------------------------------------
	var Direction = (Forward * Input_Direction.y + Right * Input_Direction.x).normalized()
#-------------------------------------------------------------------------------
	if Direction.length() > 0:
		var Target_Angle = atan2(Direction.x, Direction.z)
		var Angle_Diff = abs(wrapf(Target_Angle - Character.rotation.y, -PI, PI))
#-------------------------------------------------------------------------------
		if Angle_Diff > deg_to_rad(150):
			Current_Speed *= 0.625
#-------------------------------------------------------------------------------
		var Turn_Speed = lerp(8.0, 20.0, Angle_Diff / PI)
#-------------------------------------------------------------------------------
		if Input_Direction.y > 0:
			Turn_Speed *= 1.5
#-------------------------------------------------------------------------------
		Character.rotation.y = lerp_angle(Character.rotation.y, Target_Angle, Turn_Speed * delta)
#-------------------------------------------------------------------------------
	if Direction.length() > 0:
		Speed_Stop_Timer = 0.0
#-------------------------------------------------------------------------------
		if is_on_floor():
			Current_Speed += Accel_Rate * delta
		else:
			Current_Speed += lerp(Current_Speed, 20.0, 0.5 * delta)
		Current_Speed = clamp(Current_Speed, Constants.Min_Speed, Constants.Max_Speed)
#-------------------------------------------------------------------------------
		velocity.x = Direction.x * Current_Speed
		velocity.z = Direction.z * Current_Speed
	else:
		Speed_Stop_Timer += delta
#-------------------------------------------------------------------------------
		var Stop_Rate = Accel_Rate * 14.0
		var Horizontal_Velocity = Vector3(velocity.x, 0, velocity.z)
#-------------------------------------------------------------------------------
		Horizontal_Velocity = Horizontal_Velocity.move_toward(Vector3.ZERO, Stop_Rate * delta)
#-------------------------------------------------------------------------------
		velocity.x = Horizontal_Velocity.x
		velocity.z = Horizontal_Velocity.z
#-------------------------------------------------------------------------------
		if Speed_Stop_Timer >= Constants.Stop_Speed_Reset_Time:
			Current_Speed = 0.0
#-------------------------------------------------------------------------------
	move_and_slide()

And here’s the problem:

func _physics_process(delta: float) -> void:
	if not is_on_floor():
		if Input.is_action_pressed("Jump") and Jump_HoldTime > 0.0:
			velocity.y -= Constants.Gravity * 0.5 * delta
			Jump_HoldTime -= delta
		else:
			velocity.y -= Constants.Gravity * delta
	else:
		velocity.y = -0.1

Your gravity is not being applied consistently. This is how that same code is done in the default template.

func _physics_process(delta: float) -> void:
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

Your LLM is way overcomplicating your code. Tell it to use get_gravity() instead of Constants.Gravity And if you want to change the default grravity constant, do it in Project Settings.

i did not use an llm, the reason the jump code is like this is because i want the jump to go higher depending on how long you hold the jump button. Although i did not know about get_gravity. But anyways that isn’t the issue, i’m trying to align the player with the surface he’s walking on

Then a picture would help. Are you trying to rotate the player on a slope?

yes exactly, here:

Godot_v4.6-stable_win64_m808OY7bej

1 Like

Ah ok.

In that case you need to create a RacCast3D pointing straight down, and get collision data from it, and then used vector math to get a normal so that you can rotating the character opposite the angle of the floor.

1 Like

CharacterBody3D already gives you the ground normal. So you could build a rotation matrix by doing something like this:

var up = Vector3.UP
if is_on_floor():
	up = get_floor_normal()
var forward = -transform.basis.z.slide(up).normalized()
var right = forward.cross(up)
transform.basis = Basis(right, up, -forward)

And if you want a smooth rotation, you could replace the last line with:

var speed = 20
var alignment = Basis(right, up, -forward)
transform.basis = transform.basis.slerp(alignment, speed * delta)
1 Like