Active Ragdoll Godot 4.6

Godot Version

v4.6.3.stable.official

Question

Hello,

I recently found this video regarding animation driven active ragdolls in Godot: https://www.youtube.com/watch?v=0MHY2TDeMLM.

I thought this was really neat and decided to implement it into my project.

Unfortunately, I’ve had many issues getting this setup in v4.6 with similar results on both Jolt and the default physics engines.

To my understanding the original implementation outlined in the video is not supporting in Godot v4.3 and later. There is a pull request here to address this for v4.3 and v4.4: Update to Godot 4.3 by imberny · Pull Request #2 · CBerry22/Active-Ragdoll---Physics-Animations-in-Godot-4.0 · GitHub

I’ve copied the the PhysicalSkeletonModifier3D from the pull request into my project. See related code below:

class_name PhysicalSkeletonModifier3D extends SkeletonModifier3D

@export var target_skeleton: Skeleton3D

@export var linear_spring_stiffness: float = 1200.0
@export var linear_spring_damping: float = 40.0
@export var max_linear_force: float = 9999.0

@export var angular_spring_stiffness: float = 4000.0
@export var angular_spring_damping: float = 80.0
@export var max_angular_force: float = 9999.0

var physics_bones

var _delta: float = 1.0 / 60.0


func _ready():
	physics_bones = self.get_skeleton().get_children().filter(func(x): return x is PhysicalBone3D)


func _physics_process(delta):
	self._delta = delta


func hookes_law(
	displacement: Vector3, current_velocity: Vector3, stiffness: float, damping: float
) -> Vector3:
	return (stiffness * displacement) - (damping * current_velocity)


func _process_modification() -> void:
	for b in physics_bones:
		var target_transform: Transform3D = (
			target_skeleton.global_transform * target_skeleton.get_bone_global_pose(b.get_bone_id())
		)
		var current_transform: Transform3D = (
			global_transform * self.get_skeleton().get_bone_global_pose(b.get_bone_id())
		)
		var rotation_difference: Basis = target_transform.basis * current_transform.basis.inverse()

		var position_difference: Vector3 = target_transform.origin - current_transform.origin

		if position_difference.length_squared() > 1.0:
			b.global_position = target_transform.origin
		else:
			var force: Vector3 = hookes_law(
				position_difference,
				b.linear_velocity,
				linear_spring_stiffness,
				linear_spring_damping
			)
			force = force.limit_length(max_linear_force)
			b.linear_velocity += (force * self._delta)

		var torque = hookes_law(
			rotation_difference.get_euler(),
			b.angular_velocity,
			angular_spring_stiffness,
			angular_spring_damping
		)
		torque = torque.limit_length(max_angular_force)

		b.angular_velocity += torque * self._delta

Sadly, in v4.6 it appears that this solution is no longer applicable. In my project, this results in the physics skeleton falling to the ground and flopping around (kinda like a fish lol).

Has something changed in Godot v4.6 that would explain this? Any suggestions on how i could resolve this?

Thank you!!

1 Like