Making crate carried by player follow mouse look and collide

Godot Version

Godot Stable 4.2.1

Question

` I’m working on an fps game, and am trying to add crates that the player can carry, and move around the world, I’ve got the crate moving with the player, but it currently doesn’t follow the mouse, or interact with other objects. Just wondering what methods I could use to achieve this. The code blocks and video are below:

Crate Script:

xtends RigidBody3D

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 _process(delta):
	if is_held == true:
		self.reparent(player.carry_point)
	elif is_held == false:
		self.reparent(player.get_parent())


func hold():
	is_held = !is_held

Player Interact Script:

func _physics_process(delta):
	if collider.is_in_group("hold"):
				if collider.is_held == false:
					prompt.text = "[F] Grab " + str(collider.item_name)
				else:
					prompt.text = ""
				if Input.is_action_just_pressed("interact"):
					collider.hold()

`

1 Like

Where is your player.carry_point in relation to the Camera3D? These should basically have the same parent for this to work.

1 Like

My carry point is a child of the camera3D, the node path is Player/Head/Camera3D/CarryPoint

or interact with other objects

Setting freeze = true and the freeze_mode = FreezeMode.FREEZE_MODE_KINEMATIC on the crate when it is held should make it correctly interact with other physics objects as its moved around.

Edit: You can probably set the freeze_mode in the property inspector, as you shouldn’t need to keep switching it back and forth. Then you could do:

func hold():
    is_held = !is_held
    freeze = is_held
1 Like

Hey, I tried this, and the crate still doesn’t interact with the static objects like buildings, which are all StaticBody3D’s, and it pushes the enemy models away, which I don’t want to happen, as I want it to be not able to push things.

Ah, because you are essentially forcing the position of the crate to update by moving its parent, Godot’s physics can’t stop it from moving into static objects.

From my current understanding, moving physics objects manually can cause desync problems with the physics servers. Saying that, I’m not actually sure at all if moving the physics objects parent counts as manually moving a physics object? Perhaps that case is correctly handled by the physics server?

As you can probably tell, your problem might be a little out of my knowledge, so sorry I’m not sure I’ll be able to give you any concrete help or fixes. However these are some things I thought of (in no specific order)

  • Use a CharacterBody3D instead of a RigidBody3D for your crates and make use of its move_and_slide method.
  • Continue using RigidBody3D but instead of just attaching the carry_point use it’s physic methods (apply_*) to move it around
  • Manually check collisions and stop the position from updating, possibly in _integrate_forces method
1 Like

Hi,
I followed your advice, and made the crate a CharacterBody3D, don’t know why I didn’t think of that earlier! The crate is now moving as expected, and colliding with other objects, however it now won’t fall when I let it go. I’m hoping you can help me with that.
Here’s the current code snippet:

func _process(delta):
	velocity = Vector3.ZERO
	
	#Hold
	if is_held == true:
		self.reparent(player.carry_point)
		if global_position.distance_to(player.carry_point.global_position) > 0.1 and global_position.distance_to(player.carry_point.global_position) < 3:
			velocity = ((player.carry_point.global_position - global_position).normalized()) * 10
		elif global_position.distance_to(player.carry_point.global_position) >= 3:
			is_held = false
		move_and_slide()

	#Drop
	elif is_held == false:
		self.reparent(player.get_parent())
		if !is_on_floor():
			velocity = Vector3.DOWN
		elif is_on_floor():
			velocity = Vector3.ZERO

And here’s the video:

Thank you!

CharacterBody3D are not affected by gravity, you need to apply the gravity yourself. Add this to your “not holding” piece of code:

velocity += get_gravity()
move_and_slide()
1 Like

I put it in and it says

function "get_gravity()" not found in base self.

Here’s the code:

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

Apparently get_gravity() got implemented in 4.3, so if you’re using 4.2 you will not have it. I recommend you to upgrade Godot to the latest 4.3 or 4.4 versions, if that’s feasible for you, but if not, then you can grab the gravity from ProjectSettings instead:

	var gravity_force: float = ProjectSettings.get_setting("physics/3d/default_gravity")
	var gravity_vector: Vector3 = ProjectSettings.get_setting("physics/3d/default_gravity_vector")
	var gravity: Vector3 = gravity_force * gravity_vector

Thank you for your assistance, I downloaded Godot 4.3, and the boxes are falling now, although when I stack boxes on top of each other, then take the bottom one away, the one above doesn’t fall, and remains floating, any idea what causes this? Here is the code snippet:

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

And here is a video:

Thanks again for your help!

Check out the is_on_floor() method description, I highlighted the important part:

bool is_on_floor() const

Returns true if the body collided with the floor on the last call of move_and_slide. Otherwise, returns false. The up_direction and floor_max_angle are used to determine whether a surface is “floor” or not.

What it means is that is_on_floor() updates only when you call move_and_slide(). In your code, when the crate is on the floor, you just set the velocity to 0, and then you never call move_and_slide() again, so the is_on_floor() will also never be updated, so your crate is stuck mid-air.

		elif is_on_floor():
			velocity = Vector3.ZERO

Put all your move_and_slide() method calls outside of the if blocks, just at the complete end of the _physics_process() function (change it from _process() to _physics_process() by the way, I forgot to mention that before).
Within the if blocks you should only change the velocity, but the move_and_slide() should ideally be called every physics frame regardless of what the velocity is. If the velocity is 0, then it will just not move.

Edit: I think you don’t even need to separate the is_on_floor vs !is_on_floor scenarios, just apply the gravity all the time, that’s how it works in real world physics anyway, right? :slight_smile: Maybe if you have hundreds of these calls in your scenes that may impact the performance, but I think the Engine should be optimized enough to handle it for you - you shouldn’t worry about it until you have a real issue with performance.

Thanks, I did that, but the crates seem to have a hard time cooperating, I might just make a Area3D above the box that checks if there’s something above it, like another box or the player then won’t get picked up if there is. Thank you so much for all of your help!

2 Likes

Yup, definitely a good idea to check if there already is something stacked on top of it before allowing to move it.
Good luck with your project!

Thanks!

1 Like

I’m glad @wchc was able to help, I’ve never actually used CharacterBody3D myself so wouldn’t have known the issues!
Glad you got something working at least, and your game is looking pretty cool by the way!

2 Likes

Thank you for your help, and your kind words about my game, I’ll be sure to let you know when there’s a playable demo available!

2 Likes