RigidBody3D is very shaky for my kart controller

Godot Version

v4.2.1

I am making a kart controller for a Mario Kart-like game, it is controlled by a sphere shaped rigidbody3D that rolls around and the kart mesh just follows it. I am fairly new to Godot and not familiar with how all of the physics and whatnot work so to get a basis for it, I copied and modified some scripts online. I then added a drift system which despite the probably very poorly written code, it works pretty well. The issue with the kart is that the RigidBody for whatever reason is very shaky and I can’t seem to figure out why. I have a suspicion its because of the friction code but I really have no clue…

Its kind of hard to explain so here’s a video example of what I’m talking about:

Here’s how the kart is set up:

kartFinal.tscn - Dukkart - Godot Engine 7_16_2024 10_06_10 AM|690x362

And here’s the code for it:

extends Node3D

@onready var ball = $ball
@onready var car = $Car
@onready var ray = $Car/RayCast3D
@onready var cam_pivot = $Car/CameraPivot
@onready var anim = $Car/AnimationPlayer


var sphereOffset = Vector3(0, -.19, 0)
var acceleration = 5
var steering = 25
var turnSpeed = .7
var turnStopLimit = .75
var sidewaysFriction = .8
var inputRange = 1.0
var drifting = false
var turnInput
var reversing = false
var driftDirection
var goRight

# Called when the node enters the scene tree for the first time.
func _ready():
	ray.add_exception(ball)

func snap_position(t: Transform3D) -> void:
	ball.global_transform = t
	car.global_transform = t
	ball.linear_velocity = Vector3.ZERO
	ball.angular_velocity = Vector3.ZERO

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	
	var input = Input.get_vector("a","d","w","s")
	
	var linear_velocity: Vector3 = ball.get_linear_velocity()
	var local_linear_velocity: Vector3 = car.global_transform.basis.inverse() * linear_velocity
	
	if drifting == false:
		turnInput = -input.x
	
	if linear_velocity.length() < 20:
		ball.apply_central_force(car.global_transform.basis.z * input.y * acceleration)
	
	if ball.linear_velocity.length() > turnStopLimit:
		var new_basis: Basis = car.global_transform.basis.rotated(car.global_transform.basis.y, turnInput).orthonormalized()
		var new_basis2: Basis = car.global_transform.basis.rotated(car.global_transform.basis.y, -turnInput).orthonormalized()
		var turn_direction: float = -sign(local_linear_velocity.z)
		print(turnStopLimit)
		if reversing == false:
			car.global_transform.basis = car.global_transform.basis.slerp(new_basis, turnSpeed * turn_direction * delta)
		elif reversing == true:
			car.global_transform.basis = car.global_transform.basis.slerp(new_basis2, turnSpeed * turn_direction * delta)
		car.global_transform = car.global_transform.orthonormalized()
	
	var new_basis = car.global_transform.basis.rotated(car.global_transform.basis.y, -input.x)
	
	car.global_transform = car.global_transform.orthonormalized()
	car.global_transform = car.global_transform.basis.slerp(new_basis, turnSpeed * delta)
	
	var n: Vector3 = ray.get_collision_normal()
	var xform: Transform3D = align_with_y(car.global_transform, n.normalized() )
	car.global_transform = car.global_transform.interpolate_with(xform, 10.0 * delta)
	
	var sideways_velocity: float = local_linear_velocity.x
	var sideways_force: Vector3 = sideways_velocity * sidewaysFriction * -car.transform.basis.x
	ball.apply_central_force(sideways_force)
	
	if Input.is_action_just_pressed("drift"):
		if driftDirection == 0:
			turnInput = -input.x
		anim.play("drifthop")
		turnSpeed = 1
		driftDirection = input.x
		if driftDirection == 0:
			turnInput = -input.x
		elif driftDirection > 0:
			turnInput = -input.x - 2
		elif driftDirection < 0:
			turnInput = -input.x + 2
		print(driftDirection)
		await get_tree().create_timer(.4).timeout
		if Input.is_action_pressed("drift"):
			drifting = true
			if driftDirection == 0:
				turnInput = -input.x
			elif driftDirection <= 0:
				turnInput = -input.x + .2
			elif driftDirection >= 0:
				turnInput = -input.x - .2
			
	elif Input.is_action_just_released("drift"):
		turnInput = -input.x
		drifting = false
		turnSpeed = .7
	
	if Input.is_action_just_pressed("w"):
		reversing = false
	elif Input.is_action_just_pressed("s"):
		reversing = true
	
	car.transform.origin = ball.transform.origin + sphereOffset

func align_with_y(xform: Transform3D, new_y: Vector3) -> Transform3D:
	xform.basis.y = new_y
	xform.basis.x = -xform.basis.z.cross(new_y).normalized()
	xform.basis = xform.basis.orthonormalized()
	return xform

If anyone would be willing to help me with this it would be greatly appreciated!

What is it like if you run your code in _physics_process instead of _process?

Wow, I feel really stupid now, thank you so much that worked!!!

But theres still a small issue, for whatever reason, when you press the drift button, the car clips under the map for a split second

Heres what it looks like:

Any clue why this would be happening?

Looks better, but still weird. The flickering is new. Did you mean that by clipping under the map?

The only thing that looks a bit strange to me is the await. If I see this correctly that would mean that when you start to press the drift button, the car.transform.origin won’t be updated too for 0.4 seconds. But I’m not sure, haven’t used await a lot so far.

Maybe try commenting the await line out and see what happens. That’s the only thing I can think of atm.

I just noticed the kart scene sreenshot.

Why did you choose to make the scene root of the kart a node3d and not the rigidbody3d/ball itself? :thinking:

you can also move the await to a separate function, if you wanted the delay as a feature. Then the rest of the physics process function can continue.

Sure enough, it was the await. The purpose of that was to create a small delay during the hop before the drift actually starts. The root is a Node3D so I don’t have to have the kart as a child of the RigidBody.

I’m pretty sure I can figure the rest out from here, thank you for helping!

1 Like

Yup, it was the delay, moving it to a function works, thank you!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.