Reading updated position values after moving a AnimatableBody3D

Godot Version

v4.5.1.stable.official [f62fdbde1]

Question

I attached the following script to a Node3D node and to a AnimatableBody3D node :

extends Node3D

func _physics_process(delta: float) -> void:
	print(name)
	print("> move:", position)
	position += Vector3.FORWARD * delta
	force_update_transform()
	print("< move:", position)

The output I got was as such:

Node3D
> move:(0.0, 0.0, 0.0)
< move:(0.0, 0.0, -0.016667)
AnimatableBody3D
> move:(0.0, 0.0, 0.0)
< move:(0.0, 0.0, 0.0)

This is unexpected. How can I get the updated position of AnimatableBody3D after moving it in _physics_process? I’m implementing something similar to move_and_slide but need to adjust it to have some custom behavior.

An AnimatableBody3d will be synced with the physics server and any update to its transform will be sent to the physics server and reverted until the physics server reports the new position.

You should be using PhysicsBody3D.move_and_collide() instead.

I still can’t get an updated position in the same frame after move_and_collide().

func _process(delta: float) -> void:
	print(name)
	print("> move:", position)
	if get_node(".") is AnimatableBody3D:
		(get_node(".") as AnimatableBody3D).move_and_collide(Vector3.FORWARD * delta)
	else:
		position += Vector3.FORWARD * delta
	force_update_transform()
	print("< move:", position)
Node3D
> move:(0.0, 0.0, 0.0)
< move:(0.0, 0.0, -0.030301)
AnimatableBody3D
> move:(0.0, 0.0, 0.0)
< move:(0.0, 0.0, 0.0)
Node3D
> move:(0.0, 0.0, -0.030301)
< move:(0.0, 0.0, -0.054411)
AnimatableBody3D
> move:(0.0, 0.0, -0.030301)
< move:(0.0, 0.0, -0.030301)

Again, the AnimatableBody3D will revert back the position to an old value until the physics server syncs it back to the updated position so force_update_transform() will do nothing. You’ll get all the information about the movement and collision in the returned value from move_and_collide()

Seems like I didn’t explain well what I’m trying to do here.
To implement the desired gameplay logic, an object needs to move_and_collide, check the result and then move_and_collide again depending on the result, all within the same frame.

From what I can tell, calling move_and_collide multiple times in a frame won’t work.
In the following example, move_and_collide is being called twice and the second move_and_collide doesn’t seem to start from where previous one ended.

Here’s the code that I tested and output that I got:

extends Node3D

func _physics_process(delta: float) -> void:
	print()
	print(Engine.get_physics_frames(), "| ", name, " @ ", position)
	if Engine.get_physics_frames() == 3:
		get_tree().quit()

	var delta_move := Vector3.FORWARD * delta
	var a : AnimatableBody3D = get_node(".") as AnimatableBody3D

	if a:
		var p := position
		var k : KinematicCollision3D
		var t : Vector3
		print("> move:", p)
		k = a.move_and_collide(delta_move)
		t = k.get_travel() if k else delta_move
		p += t
		k = a.move_and_collide(delta_move)
		t = k.get_travel() if k else delta_move
		print("< move:", p + t)
	else:
		print("> move:", position)
		position += delta_move
		position += delta_move
		print("< move:", position)

1| Node3D @ (0.0, 0.0, 0.0)
> move:(0.0, 0.0, 0.0)
< move:(0.0, 0.0, -0.033333)

1| AnimatableBody3D @ (0.0, 0.0, 0.0)
> move:(0.0, 0.0, 0.0)
< move:(0.0, 0.0, -0.033333)

2| Node3D @ (0.0, 0.0, -0.033333)
> move:(0.0, 0.0, -0.033333)
< move:(0.0, 0.0, -0.066667)

2| AnimatableBody3D @ (0.0, 0.0, -0.016667)
> move:(0.0, 0.0, -0.016667)
< move:(0.0, 0.0, -0.05)

3| Node3D @ (0.0, 0.0, -0.066667)
> move:(0.0, 0.0, -0.066667)
< move:(0.0, 0.0, -0.1)

3| AnimatableBody3D @ (0.0, 0.0, -0.033333)
> move:(0.0, 0.0, -0.033333)
< move:(0.0, 0.0, -0.066667)

Try using PhysicsBody3D.test_move() instead updating the from parameter accordingly.

1 Like

thanks, the following works:

extends Node3D

func _physics_process(delta: float) -> void:
	print()
	print(Engine.get_physics_frames(), "| ", name, " @ ", position)
	if Engine.get_physics_frames() == 3:
		get_tree().quit()

	var delta_move := Vector3.FORWARD * delta
	var a : AnimatableBody3D = get_node(".") as AnimatableBody3D

	if a:
		var t := transform
		var k : KinematicCollision3D = KinematicCollision3D.new()
		print("> move:", t.origin)

		a.test_move(t, delta_move, k)
		t.origin += k.get_travel()

		a.test_move(t, delta_move, k)
		t.origin += k.get_travel()

		print("< move:", t.origin)
		transform = t
	else:
		print("> move:", position)
		position += delta_move
		position += delta_move
		print("< move:", position)