Godot Version
Godot Engine v4.4.1.stable.debian4.4.1+ds-1
Question
I’m woking on a 3rd person game where players are able to walk up and along certain walls and I’ve run into a couple road blocks regarding my methods of movement. Firstly and most importantly is the velocity dampening. Currently I am multiplying the global velocity by a number between 0 and 1 every physics update to prevent infinite acceleration. However I would only idealy want this to work on the player’s local X and Z axis to allow gravity to continue accelerating and allow for more floaty jumps. Below is the relevant code:
extends CharacterBody3D
@export var moveSpeed: float
#accel and moveSpeed are currently both 10. I will likely remove accel in the future
@export var accel: float
@export var jumpVel: float
@export var staticFriction: float
#this is a RayCast3D for identifying the mesh normal directly under the player
@export var groundCast: Better_Ground_Cast
#This is a ShapeCast in the shape of a cylinder for checking if the player is on the ground or not
@export var groundedCast: Grounding_Cast
@export var camera: SpringArm3D
#(0, -10, 0)
@export var gravity: Vector3
var tempHit: Vector3
var xform : Transform3D
var aim = transform.basis
var forward = -aim.z
var backward = aim.z
var left = -aim.x
var right = aim.x
var up = aim.y
var down = -aim.y
func _physics_process(delta: float) -> void:
var curSpeedX: float
var curSpeedZ: float
var moveDirection: Vector3
#groundCast.hit is the mesh's normal
if(groundCast.hit != null):
velocity += groundCast.hit * gravity.y * delta
tempHit = groundCast.hit
else:
velocity += tempHit * gravity.y * delta
if Input.is_action_just_pressed("jump") && groundedCast.isGrounded == true:
velocity += jumpVel * tempHit
if(Input.get_action_strength("moveUp")) != 0:
curSpeedZ = Input.get_action_strength("moveUp") * accel
if(Input.get_action_strength("moveDown")) != 0:
curSpeedZ = Input.get_action_strength("moveDown") * accel * -1
if(Input.get_action_strength("moveRight")) != 0:
curSpeedX = Input.get_action_strength("moveRight") * accel
if(Input.get_action_strength("moveLeft")) != 0:
curSpeedX = Input.get_action_strength("moveLeft") * accel * -1
moveDirection = ((forward * curSpeedZ) + (right * curSpeedX));
moveDirection = moveDirection.rotated(aim.y.normalized(), camera.rotation.y)
xform = transform.basis
if(groundCast.hit != null):
xform.basis.y = groundCast.hit
if(groundCast.hit != null):
xform.basis.x = -xform.basis.z.cross(groundCast.hit)
xform.basis = xform.basis.orthonormalized()
transform.basis = xform.basis
update_rotations()
velocity += (moveDirection * delta).normalized()
velocity *= staticFriction
move_and_slide()
func update_rotations():
aim = global_transform.basis
forward = -aim.z
backward = aim.z
left = -aim.x
right = aim.x
up = aim.y
down = -aim.y
Also of note, after rotating the player, the player’s basis on the X and Z gets displaced ever so slightly (more so after each subsequent rotation); is there a way to fix that?
Before Rotation:
![]()
After rotating roughly 90 degrees positive in Z, and then back again:
![]()
Example scene; would upload a video of it but am currently barred from doing so
One last thing related to this question is that the custom gravity I wrote does not pull the player perpendicularly to the ground they are standing on when rotated (player slides around? See example screenshots). I thought this was odd, since this is actually formerly a project I was working on in Unity, where that exact same method of gravity worked fine. I’m assuming that it’s not working in Godot the same is possibly due to me applying the values directly to velocity? Although that worked fine in Unity, it could be the player sliding along the ground for whatever reason. Regardless, it would help to understand dampening the local X and Z axis, and that would probably lead me to fixing the gravity issue.
Start:
On Ramp:
5 Seconds of no input later:
Still a bit shaky on how to properly manipulate basis, which could entirely just be the issue. Help is appreciated!



