Initially positioning a ragdoll in 4.3

Godot Version

4.3

Question

Hi there. In Godot 4.2 I could initially position a ragdoll in a certain pose of animation (for example, sitting in the seat of a vehicle), by doing this:

physical_skeleton.physical_bones_stop_simulation()
	animation_player.play(animation_name)
	animation_player.advance(0)
	physical_skeleton.physical_bones_start_simulation()

However, I can’t get this to work with the new PhysicalBoneSimulator3D in 4.3. The above code just remains in the T pose (even when I change to calling PhysicalBoneSimulator3D.physical_bones_stop_simulation etc)

The only code I can get to work is this :

	physical_bone_simulator.physical_bones_stop_simulation()
	animation_player.play(animation_name)
	await get_tree().process_frame
	physical_bone_simulator.physical_bones_start_simulation()

i.e. waiting one simulation tick so the animation renders on the skeleton, and only then re-enabling the physical bone simulation.

This kind of works but creates other problems for me. For example, if my ragdoll is seated in a vehicle, I also have to freeze the vehicle for one frame too otherwise the vehicle is affecting by gravity or other forces during that tick and the ragdoll will then be positioned incorrectly with respect to it. And freezing the vehicle is only possible during initialisation of a level; I wouldn’t want to freeze a vehicle during gameplay.

I’ve tried other solutions too like manually setting each bone based on the animation data but even that seems to only work if I wait for one frame.

So is there a way I can do this in 4.3, instantly positioning a ragdoll, without waiting frame?

2 Likes

find a solution to this? thanks

not really, I’m just using the “wait one simulation tick”.

for example:

## Ragdoll state for sitting in a seat.
## If the parent body is a RigidBody3D, the parent will be frozen briefly until after the first process.
extends BaseRagdollState

## Animation player of the physical skeleton, where the stationary pose will be found.
@export var animation_player : AnimationPlayer
## Name of the static pose (animation name).
@export var animation_name := ""
## Legs are pinned to the seat.
@export var leg_left : PhysicalBone3D
## Legs are pinned to the seat.
@export var leg_right : PhysicalBone3D

var _parent_body = null
var _process_ticks = 0
var _joints = []


func _state_process(delta):
	# Allow one tick before starting the ragdoll simulation again
	# to allow skeleton to be positioned according to the animation
	# This is not a great solution, as it means the parent might need to be frozen too; see below.
	# And there might be multiple Seated states freezing/unfreezing the same body.
	# Also if Human was spawned in a vehicle seat during game might be issue with vehicle being frozen
	# during game.
	# Ideally animation poses could be applied instantly to the ragdoll as in Godot 4.2 but
	# I'm not sure it's possible now with 4.3
	if _process_ticks == 1:
		_start_sim()
		_add_joint(_parent_body, physical_skeleton.spine)
		_add_joint(_parent_body, leg_left)
		_add_joint(_parent_body, leg_right)
	
	_process_ticks += 1


func _stop_sim():
	# Freeze the parent too for one tick, so it retains the same expected position after
	# the physical skeleton has been processed once.
	if _parent_body is RigidBody3D:
		_parent_body.freeze = true

	# Set the specific static pose,
	physical_skeleton.bone_simulator.physical_bones_stop_simulation()


func _start_sim():
	physical_skeleton.bone_simulator.physical_bones_start_simulation()

	if _parent_body is RigidBody3D:
		_parent_body.freeze = false


# physical_skeleton must be in scene before this state can be entered
func _enter(data = {}):
	super(data)

	_process_ticks = 0
	_parent_body = data.parent_body

	# Set the specific static pose,
	_stop_sim()
	animation_player.play(animation_name)


func _exit():
	for joint in _joints:
		joint.queue_free()
	
	_joints = []

	# In case the single tick did not pass and _state_process did not already start sim
	_start_sim()

	super()


func _add_joint(parent_body, physical_bone):
	var joint = SliderJoint3D.new()
	joint.set("linear_limit/lower_distance", -0.01)
	joint.set("linear_limit/upper_distance", 0.01)
	parent_body.add_child(joint)
	joint.global_transform = physical_bone.global_transform
	joint.set_node_a(physical_bone.get_path())
	joint.set_node_b(parent_body.get_path())
	_joints.append(joint)

(ignore the joint related stuff. It’s for a character in bus which is joined to the seat by some joints but otherwise is physically simulated. The “parent_body” isn’t an actual parent to the node but just another physics body this character spawns alongside - in this case, the bus)

1 Like

wow thanks for the code and comments! will definitely do for now ha