How do I stop a RigidBody?

Godot Version

4.3.stable

Question

How can I stop a RigidBody without directly setting its linear velocity?

I have a ball with a mass of 1 kg, and there is no damping (linear or angular) or friction in the world. The ball’s rotation is frozen.

The gravity_scale of the body is 4, but since there’s no friction, it shouldn’t affect the horizontal movement.

The setup consists of:

  • RigidBody3D
    • MeshInstance3D (Sphere)
    • CollisionShape (Sphere)
    • Timer

My goal is to stop the ball when the timer runs out, but it’s not working. Why is that?

Bonus question: How can I stop the ball over a specified duration? For example, if I want the ball to decelerate to 0 over 5 seconds, regardless of its initial velocity, how would I do that?

This my test script:

extends RigidBody3D

func _process(delta: float) -> void:	
	if $Timer.is_stopped():
		# The ball turns blue to indicate that it is stopping
		$MeshInstance3D.mesh.material.albedo_color = Color.BLUE;
		# Since the mass is 1, shouldn't applying a negative linear velocity immediately stop the body?
		apply_central_force(-linear_velocity * delta);
	else:
		apply_central_force(Vector3.FORWARD * 100 * delta);

Thank you!

1 Like

Hi :handshake: It’s important to note that applying a force is not an instantaneous change in the object’s speed, but a gradual effect.
Your Problem is that you’re applying a force to slow down the object in _process(), but it doesn’t stop the object immediately.

Problems:

  1. Applying a Force: Using apply_central_force(-linear_velocity * delta) will slow the object down. But the force works as a gradual change in speed, not an instant stop.

  2. Timer: It looks like you’re using is_stopped() to check the state of the timer, but that doesn’t handle the stop correctly when the timer expires.

Possible solutions:

Stopping the object instantly when the timer expires
Instead of applying forces, it is better to use the sleep function, which will freeze the object’s speed:

extends RigidBody3D

func _process(delta: float) -> void:
if $Timer.is_stopped():
# Change the color of the ball to blue
$MeshInstance3D.mesh.material.albedo_color = Color.BLUE
# Stop the object completely
linear_velocity = Vector3.ZERO
angular_velocity = Vector3.ZERO
else:
apply_central_force(Vector3.FORWARD * 100 * delta)

This way, as soon as the timer expires, you can force the body’s speed to zero, and the ball will stop instantly.

Slowing Down an Object Gradually
To slow down a ball over a certain amount of time (e.g. 5 seconds), you can do the following:

  1. Add a slowdown time variable.
  2. Gradually decrease the speed of the object.
extends RigidBody3D

var slow_down_time = 5.0 # Braking time in seconds
var time_passed = 0.0
var initial_velocity = Vector3.ZERO

func _ready():
initial_velocity = linear_velocity # Save the initial velocity

func _process(delta: float) -> void:
if !$Timer.is_stopped():
apply_central_force(Vector3.FORWARD * 100 * delta)
else:
# Change the ball color to blue
$MeshInstance3D.mesh.material.albedo_color = Color.BLUE

# Gradually slow down the ball
if time_passed < slow_down_time:
time_passed += delta
var factor = 1.0 - (time_passed / slow_down_time)
linear_velocity = initial_velocity * factor
else:
# Stop the ball completely after the deceleration time
linear_velocity = Vector3.ZERO
angular_velocity = Vector3.ZERO

How it works:

  • initial_velocity stores the initial velocity of the ball.
  • Over 5 seconds (slow_down_time), the ball’s velocity decreases linearly to zero.
  • After the ball has completely slowed down, its velocity is set to Vector3.ZERO and it stops.

This way you can stop the ball either instantly or smoothly over a given time.

I’ll hope this way it’s help to you

Code It’s not included TABS sorry :cry:

Thank you for the response, but unfortunately I cannot set the linear_velocity directly. I am looking for the correct formula to accurately calculate the force needed to stop the body.

1 Like

In this case, since you can’t directly set linear_velocity, you need to calculate the force required to smoothly decelerate the body. This can be done using Newton’s second law:

𝐹 =𝑚𝑎

where is the force 𝐹 - is equal to the mass of the object
(𝑚) - multiplied by (𝑎) - acceleration.

So You want the body to stop in a fixed time, say (𝑡)
seconds. To do this, you need to calculate the acceleration that will allow the body to slow down to a complete stop.

Steps:

  1. Definition of initial speed: Let the initial speed of the object be 𝑣(zero).

  2. Stopping time: You want to stop the object in time 𝑡

  3. Calculate acceleration: The acceleration required to stop the object can be calculated using the formula:
    formula_01
    (Acceleration is negative because it is directed against the motion.)

  4. Deceleration force: Now, apply Newton’s law to calculate the force:

Implementation example:

extends RigidBody3D

var slow_down_time = 5.0  # Time to decelerate
var initial_velocity = Vector3.ZERO

func _ready():
	initial_velocity = linear_velocity  # Save the initial velocity

func _process(delta: float) -> void:
	if !$Timer.is_stopped():
		apply_central_force(Vector3.FORWARD * 100 * delta)
	else:
		# Change the ball color to blue
		$MeshInstance3D.mesh.material.albedo_color = Color.BLUE
		
		# Calculate the deceleration force
		var force = -mass * initial_velocity / slow_down_time
		
		# Apply the deceleration force
		apply_central_force(force * delta)

I’m not Expert in programming so I hope I gave you direction :slight_smile:

Thanks for the help but this code is completely wrong. In ready the linear_velocity will always be 0, as said the mass of the body is 1.

So the result of this code will always be -1 * 0 / 5 = 0.

Thank you for the help but I don’t think ChatGPT can help us here :sweat_smile:

Personally, i wouldnt use force to stop something dead on the spot. I would just set the position of where it was stopped directly.

you mention a “sleep” function but nowhere in your code does it say sleep. I’m so confused…