How to make CharacterBody3D stay stable on rotating and moving platforms

Trying to make simple vehicle (CharacterBody3D or AnimatedBody3D following Path3D) however another CharacterBody3D (player character) is never stable standing on such moving platforms. It slides and moves around eventually falling off.

Scene and code setup

image

Platform in video is ‘Car’ which is simple CharacterBody3D with basic movement script.
AnimatableBody3D is another test, it was suggested to use this for moving platforms but its same issue. Tried all the friction settings too

Character controller.gd
extends CharacterBody3D

const SPEED = 4
const JUMP_VELOCITY = 4.5
var last_angle = 0

func _physics_process(delta: float) -> void:
	if not is_on_floor():
		velocity += get_gravity() * delta
		
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY
	
	var input_dir := Input.get_vector("left", "right", "forward", "backward")
	var world_direction = transform.basis * Vector3(input_dir.x, 0, input_dir.y)
	var direction = world_direction.normalized()

        #quick test in how to keep rotation with vehicle
	var angle_change = $"../Car".rotation.y - last_angle 
	last_angle = $"../Car".rotation.y
	$Humans_Master.rotation.y += angle_change

	if direction:
		velocity.x = (direction.x * SPEED) 
		velocity.z = (direction.z * SPEED)
		$Humans_Master.rotation.y = lerp_angle($Humans_Master.rotation.y, atan2(input_dir.x, input_dir.y), delta * 12)
	else:
		velocity.x = 0
		velocity.z = 0
		
	$Humans_Master/AnimationTree.set("parameters/conditions/idle", input_dir == Vector2.ZERO && is_on_floor())
	$Humans_Master/AnimationTree.set("parameters/conditions/run", input_dir != Vector2.ZERO  && is_on_floor())
	move_and_slide()

minimal vehicle controller.gd
extends CharacterBody3D

@export var gravity = 0
@export var wheel_base = 2
@export var steering_limit = 10.0
@export var engine_power = 6.0
@export var braking = -9.0
@export var friction = -2.0
@export var drag = -2.0
@export var max_speed_reverse = 3.0

var acceleration = Vector3.ZERO
var steer_angle = 0.0

func _physics_process(delta):
	get_input()
	apply_friction(delta)
	calculate_steering(delta)
		
	acceleration.y = gravity
	velocity += acceleration * delta
	move_and_slide()
	
func apply_friction(delta):
	if velocity.length() < 0.2 and acceleration.length() == 0:
		velocity.x = 0
		velocity.z = 0
	var friction_force = velocity * friction * delta
	var drag_force = velocity * velocity.length() * drag * delta
	acceleration += drag_force + friction_force
	
func calculate_steering(delta):
	var rear_wheel = transform.origin + transform.basis.z * wheel_base / 2.0
	var front_wheel = transform.origin - transform.basis.z * wheel_base / 2.0
	rear_wheel += velocity * delta
	front_wheel += velocity.rotated(transform.basis.y, steer_angle) * delta
	var new_heading = rear_wheel.direction_to(front_wheel)

	var d = new_heading.dot(velocity.normalized())
	if d > 0:
		velocity = new_heading * velocity.length()
	if d < 0:
		velocity = -new_heading * min(velocity.length(), max_speed_reverse)
	look_at(transform.origin + new_heading, transform.basis.y)

func get_input():
	var turn = Input.get_action_strength("steer_left")
	turn -= Input.get_action_strength("steer_right")
	steer_angle = turn * deg_to_rad(steering_limit)
	$"Back 1".rotation.y = steer_angle*2
	$"Back 2".rotation.y = steer_angle*2
	acceleration = Vector3.ZERO
	if Input.is_action_pressed("accelerate"):
		acceleration = -transform.basis.z * engine_power
	if Input.is_action_pressed("brake"):
		acceleration = -transform.basis.z * braking

Ignore cam jitter (another problem cant solve with smoothing addons or interpolation)

See how character slides left side and right side when turning and it slowly slides backwards eventually falling off at the end. During driving player character velocity is 0.

Is there a way to make character perfectly stable on such platforms while retaining ability to still move around on it while its moving?

This works in 2d and pretty sure works for 3d too, just basically change the object that is floating/moving motion mode from grounded to floationg:

1 Like

Changing platforms to floating makes no difference. I did extensive search on this issue and people have this same problem for +4 years now and there is no proper solution. I can do this functionality in unity easy but in godot its big struggle

Mmm, have you tried to add the velocity of the moving object to the player?