Keeping 3d items from moving inside of a container (e.g. Car's trunk, Shopping basket)

Godot Version

4.2.2

Question

I’m trying to do something similar to The Long Drive where the player can place items inside the car’s trunk and when place they stay still until the player picks them up again.

The items have their own bodies that collide with other items, can fall and roll on the ground but will freeze when inside a container.

So far I’ve used this project (video demo here) as a base and created a “prop” that looks like some kind of crate/basket.

The basket extends the LightProp (from the project above) that makes it so I can pick it up, move it around or throw it .

I’ve then added an Area3D to detect when other objects are inside the basket and when I detect a collision I freeze the body that enters the basket but that blocks the basket from moving since the item’s body is blocking it.

I’ve tried putting the item to sleep (both with static and kinematic options) but that doesn’t do it either since the body is still moving around when I move the basket.

Another thing I tried was to set the mass of the item to 0, set the item to sleep and reparent the item to the basket so that it would “follow” the basket’s movements but I wasn’t sure how to properly implement it.

This is part of the code from my basket’s script

extends LightProp

var bodies: Dictionary

func _process(delta: float) -> void:
	super._process(delta)

	for body in bodies:
		if body is LightProp and not body.prop_container and not body.sleeping:
			
			# Check if the item's body has moved since last time
			if bodies[body] != null and body.transform.is_equal_approx(bodies[body]):
				body.reparent(self)
				body.sleeping = true
				body.mass = 0

			bodies[body] = Transform3D(body.transform)

func _on_container_body_entered(body: Node3D) -> void:
	if body != self:
		bodies[body] = null

Is there a way to handle this?

I tried using implementing _integrate_forces on my item’s script to zero the linear velocity but it still doesn’t work well, the items just floats mid air

func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
	if locked:
		state.linear_velocity = Vector3.ZERO
		state.angular_velocity = Vector3.ZERO

locked is set to true in the script above together with custom_integrator instead sleeping

That’s difficult. Physics bodies don’t like when you mess with them directly. They either like to be simulated all the time or frozen in place (static), but switching between these two states sucks. Even doing set_physics_process(false) isn’t that good.

I’d probably remove the rigid bodies of the items once you get in the car or start pushing a cart. Reparent the meshes to the car, so that they appear to stay in place and ride around freely :slight_smile: And then when you get out of the car, create a new Rigidbody for each of them and reparent the meshes to it again.

Just an idea, lmk if that works for you

I tried what @struhy_xd was suggesting and it does work visually but I still need to have collisions so that items cannot overlap each others.

I tried to replace the RigidBody with a StaticBody at run-time with Node.replace_by and reparent the item to the basket’s Node and it worked but still had issues.

After doing some tests I figured it was because I was pushing the item to the bottom of the basket and there were some issues with the basket and the item’s collider, so I set the item’s collision_layer to 0 and tried to keep the RigidBody and set the freeze flag again and it did work!

For future reference:

What I’m trying to do: Have a basket that I can pick up and physically and freely place other bodies inside it.

What I did to make it work:

  • The basket needs an area that can detect other Items inside it
  • Whenever an item is placed inside wait for the item to stop moving (or have a delay since some items are gonna keep rolling)
  • Freeze the item’s Rigidbody and set the mode to FREEZE_MODE_STATIC
  • Remove the basket’s collision layer from the item’s collision mask
  • Reparent the item’s node to the basket

The item should sit inside the basket without moving. Some sample code from the container script:

# type: Dictionary<RigidBody, Transform3D>
var bodies: Dictionary

func _process(delta: float) -> void:
	super._process(delta)

	for body in bodies:
		if not body.freeze:
			if bodies[body] != null and body.transform.is_equal_approx(bodies[body]):
				body.freeze = true
				body.freeze_mode = RigidBody3D.FREEZE_MODE_STATIC
				body.collision_layer = 0
				body.reparent(self)

			bodies[body] = Transform3D(body.transform)

func _on_container_body_entered(body: Node3D) -> void:
	if body != self::
		bodies[body] = null
1 Like

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