Crates not falling correctly

Godot Version

Godot 4.3 Stable

Question

Hi, I’ve recently updated to Godot 4.3, and am trying to understand how physics work with CharacterBody3Ds. I have crates that I can pick up and drop, and when I drop them, they use gravity, but as soon as a single part of them touches the “floor” they don’t continue to be affected by gravity, leading to some impossible situations. Here is the code for the crate that can be picked up:

extends CharacterBody3D

var item_name = "Crate"
var is_held = false
@onready var player = $"../Player"

# Called when the node enters the scene tree for the first time.
func _ready():
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(delta):
	velocity = Vector3.ZERO
	
	#Hold
	if is_held == true:
		self.reparent(player.carry_point)
		global_rotation.x = 0
		global_rotation.z = 0
		if global_position.distance_to(player.carry_point.global_position) > 0.1 and global_position.distance_to(player.carry_point.global_position) < 1:
			velocity = ((player.carry_point.global_position - global_position).normalized()) * 10
		elif global_position.distance_to(player.carry_point.global_position) >= 1:
			is_held = false

	#Drop
	elif is_held == false:
		self.reparent(player.get_parent())
		if !is_on_floor():
			velocity += get_gravity()/1.8
		elif is_on_floor():
			velocity = Vector3.ZERO
	
	move_and_slide()


func hold():
	is_held = !is_held

And here’s a video showing the outcome:

Any help would be appreciated. Thanks!

You should need to use RigidBody3D for the best result (realistic physics)

2 Likes

Character bodies are not purely physics driven objects like a RigidBody would be. Your code does not specify to rotate the box based on it’s floor collision, so it does not rotate off the other box.

2 Likes

I originally had it as a RigidBody3D, but then “velocity” doesn’t work, as it isn’t declared.

I think there are many tutorials on YouTube on drag and drop physics, you can check them.
Otherwise RigidBody3D uses force as velocity, but you can also define the velocity in a RigidBody3D like this:

var velocity = Vector3.ZERO

func _ready():
    gravity_scale = 0.0

func _physics_process(delta):
    move_and_slide(delta) #call it whenever needed

func move_and_slide(delta):
    position += velocity * delta

But I am not sure it will work and also it is not the proper way, so I consider to look for some tutorials on YouTube.

Here, I found a good tutorial on it, consider to check it.

Hi, I used this tutorial, and it works for the most past, unfortunately, the crate jitters quite violently once it reaches the correct location. Here’s the current code snippet of code:

func _physics_process(delta):
	
	#Hold
	if is_held == true:
		self.reparent(player.carry_point)
		global_rotation.x = 0
		global_rotation.z = 0
		if global_position.distance_to(player.carry_point.global_position) > 0.1 and global_position.distance_to(player.carry_point.global_position) < 1:
			linear_velocity = ((player.carry_point.global_position - global_position).normalized()) * 10

		elif global_position.distance_to(player.carry_point.global_position) >= 1:
			is_held = false

And here’s a video showing what’s happening:

Thanks for your help!

If you need to change the rigid body’s state directly (like position), you should rather use _integrate_forces() instead of _physics_process().

This can fix your problem (am not 100% sure though).
But ideally you should rather use apply_force() etc. rather than directly changing position.

Use limit_length(1.0) instead of normalized()

or better limit_length(10.0) instead of normalized() * 10

This should also remove the if statement before it with many distance_to functions

Hi, I did this, and it works, but the crate moves incredibly slow now, here’s the code,

#Hold
	if is_held == true:
		self.reparent(player.carry_point)
		global_rotation.x = 0
		global_rotation.z = 0
		if global_position.distance_to(player.carry_point.global_position) > 0.1 and global_position.distance_to(player.carry_point.global_position) < 1:
			linear_velocity = ((player.carry_point.global_position - global_position).limit_length(1.0))
		elif global_position.distance_to(player.carry_point.global_position) >= 1:
			is_held = false

Yes you removed the 10 times force multiplier, I’d still recommend removing the if statement above.

# storing long difference calculation
var difference: Vector3 = player.carry_point.global_position - global_position

# removed if statement, if the length is 0.1 or lower it will move very slowly
linear_velocity = difference.limit_length(1.0) * 10

# gettings the squared length is faster
if difference.length_squared() >= 1:
	is_held = false
1 Like

Hi, this works well for carrying the object, however, if the object hits something, it will gain rotational velocity, which never stops, is there a way to fix this? Video below:

The default angular damp is very low, you could change your project settings, or just for the boxes you can override/combine angular damping.

If you want to keep the angular damp you could set the angular_velocity to Vector3.ZERO while it’s held

if is_held:
    angular_velocity = Vector3.ZERO
3 Likes

Thanks, this fixed it