169 lines
6.6 KiB
GDScript
169 lines
6.6 KiB
GDScript
extends Node3D
|
|
class_name ProceduralRagdoll
|
|
|
|
var torso_body: RigidBody3D
|
|
|
|
func build_ragdoll(color: Color) -> void:
|
|
var mat := StandardMaterial3D.new()
|
|
mat.albedo_color = color
|
|
mat.roughness = 0.8
|
|
|
|
# Create bodies
|
|
torso_body = _create_body(Vector3(0.4, 0.7, 0.25), mat, Vector3(0, 1.15, 0), 20.0, "Torso")
|
|
var head_body = _create_body(Vector3(0.25, 0.25, 0.25), mat, Vector3(0, 1.65, 0), 5.0, "Head")
|
|
|
|
var upper_arm_l_body = _create_body(Vector3(0.12, 0.35, 0.12), mat, Vector3(-0.28, 1.275, 0), 3.0, "UpperArmL")
|
|
var lower_arm_l_body = _create_body(Vector3(0.1, 0.35, 0.1), mat, Vector3(-0.28, 0.925, 0), 2.0, "LowerArmL")
|
|
var upper_arm_r_body = _create_body(Vector3(0.12, 0.35, 0.12), mat, Vector3(0.28, 1.275, 0), 3.0, "UpperArmR")
|
|
var lower_arm_r_body = _create_body(Vector3(0.1, 0.35, 0.1), mat, Vector3(0.28, 0.925, 0), 2.0, "LowerArmR")
|
|
|
|
var thigh_l_body = _create_body(Vector3(0.15, 0.45, 0.15), mat, Vector3(-0.12, 0.625, 0), 5.0, "ThighL")
|
|
var calf_l_body = _create_body(Vector3(0.13, 0.45, 0.13), mat, Vector3(-0.12, 0.175, 0), 3.0, "CalfL")
|
|
var thigh_r_body = _create_body(Vector3(0.15, 0.45, 0.15), mat, Vector3(0.12, 0.625, 0), 5.0, "ThighR")
|
|
var calf_r_body = _create_body(Vector3(0.13, 0.45, 0.13), mat, Vector3(0.12, 0.175, 0), 3.0, "CalfR")
|
|
|
|
add_child(torso_body)
|
|
add_child(head_body)
|
|
add_child(upper_arm_l_body)
|
|
add_child(lower_arm_l_body)
|
|
add_child(upper_arm_r_body)
|
|
add_child(lower_arm_r_body)
|
|
add_child(thigh_l_body)
|
|
add_child(calf_l_body)
|
|
add_child(thigh_r_body)
|
|
add_child(calf_r_body)
|
|
|
|
var is_server = not multiplayer.has_multiplayer_peer() or multiplayer.is_server()
|
|
var rep = SceneReplicationConfig.new()
|
|
for child in get_children():
|
|
if child is RigidBody3D:
|
|
rep.add_property(NodePath(str(child.name) + ":position"))
|
|
rep.add_property(NodePath(str(child.name) + ":rotation"))
|
|
if not is_server:
|
|
child.freeze = true
|
|
child.freeze_mode = RigidBody3D.FREEZE_MODE_KINEMATIC
|
|
|
|
var sync = MultiplayerSynchronizer.new()
|
|
sync.name = "RagdollSync"
|
|
sync.replication_config = rep
|
|
add_child(sync)
|
|
|
|
# Need to wait a frame for paths to be valid before creating joints
|
|
call_deferred("_setup_joints", head_body,
|
|
upper_arm_l_body, lower_arm_l_body,
|
|
upper_arm_r_body, lower_arm_r_body,
|
|
thigh_l_body, calf_l_body,
|
|
thigh_r_body, calf_r_body)
|
|
|
|
func _setup_joints(head_body,
|
|
u_arm_l, l_arm_l,
|
|
u_arm_r, l_arm_r,
|
|
thigh_l, calf_l,
|
|
thigh_r, calf_r) -> void:
|
|
|
|
# Neck
|
|
_create_joint(torso_body, head_body, Vector3(0, 1.5, 0))
|
|
|
|
# Shoulders
|
|
_create_joint(torso_body, u_arm_l, Vector3(-0.28, 1.45, 0))
|
|
_create_joint(torso_body, u_arm_r, Vector3(0.28, 1.45, 0))
|
|
|
|
# Hips
|
|
_create_joint(torso_body, thigh_l, Vector3(-0.12, 0.85, 0))
|
|
_create_joint(torso_body, thigh_r, Vector3(0.12, 0.85, 0))
|
|
|
|
# Elbows (Hinge, bends forwards only)
|
|
_create_hinge(u_arm_l, l_arm_l, Vector3(-0.28, 1.10, 0), -PI * 0.8, 0.0)
|
|
_create_hinge(u_arm_r, l_arm_r, Vector3(0.28, 1.10, 0), -PI * 0.8, 0.0)
|
|
|
|
# Knees (Hinge, bends backwards only)
|
|
_create_hinge(thigh_l, calf_l, Vector3(-0.12, 0.40, 0), 0.0, PI * 0.8)
|
|
_create_hinge(thigh_r, calf_r, Vector3(0.12, 0.40, 0), 0.0, PI * 0.8)
|
|
|
|
func apply_initial_velocities(linear_vel: Vector3, impulse: Vector3) -> void:
|
|
for child in get_children():
|
|
if child is RigidBody3D:
|
|
child.linear_velocity = linear_vel
|
|
# Add random tumbling velocity so they don't fall perfectly still
|
|
child.angular_velocity = Vector3(randf_range(-5, 5), randf_range(-5, 5), randf_range(-5, 5))
|
|
|
|
# Apply the weapon impulse mostly to the torso so it yanks the limbs
|
|
if is_instance_valid(torso_body):
|
|
torso_body.apply_central_impulse(impulse * 2.0)
|
|
|
|
func _create_body(size: Vector3, mat: Material, pos: Vector3, mass: float, bname: String) -> RigidBody3D:
|
|
var body = RigidBody3D.new()
|
|
body.name = bname
|
|
body.position = pos
|
|
body.mass = mass
|
|
|
|
# Damping prevents insane spinning and explosion
|
|
body.linear_damp = 0.5
|
|
body.angular_damp = 2.0
|
|
|
|
var shape = CollisionShape3D.new()
|
|
var box_shape = BoxShape3D.new()
|
|
box_shape.size = size
|
|
shape.shape = box_shape
|
|
body.add_child(shape)
|
|
|
|
var mesh_inst = MeshInstance3D.new()
|
|
var box_mesh = BoxMesh.new()
|
|
box_mesh.size = size
|
|
box_mesh.material = mat
|
|
mesh_inst.mesh = box_mesh
|
|
mesh_inst.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_ON
|
|
body.add_child(mesh_inst)
|
|
|
|
return body
|
|
|
|
func _create_joint(node_a: RigidBody3D, node_b: RigidBody3D, pos: Vector3) -> void:
|
|
var joint = Generic6DOFJoint3D.new()
|
|
add_child(joint)
|
|
joint.position = pos
|
|
joint.node_a = node_a.get_path()
|
|
joint.node_b = node_b.get_path()
|
|
|
|
# Free angular axes to allow flopping, lock linear
|
|
joint.set_flag_x(Generic6DOFJoint3D.FLAG_ENABLE_LINEAR_LIMIT, true)
|
|
joint.set_flag_y(Generic6DOFJoint3D.FLAG_ENABLE_LINEAR_LIMIT, true)
|
|
joint.set_flag_z(Generic6DOFJoint3D.FLAG_ENABLE_LINEAR_LIMIT, true)
|
|
|
|
# Restrict angular limits so it doesn't bend 360 degrees
|
|
joint.set_flag_x(Generic6DOFJoint3D.FLAG_ENABLE_ANGULAR_LIMIT, true)
|
|
joint.set_param_x(Generic6DOFJoint3D.PARAM_ANGULAR_LOWER_LIMIT, -PI * 0.4)
|
|
joint.set_param_x(Generic6DOFJoint3D.PARAM_ANGULAR_UPPER_LIMIT, PI * 0.4)
|
|
|
|
joint.set_flag_y(Generic6DOFJoint3D.FLAG_ENABLE_ANGULAR_LIMIT, true)
|
|
joint.set_param_y(Generic6DOFJoint3D.PARAM_ANGULAR_LOWER_LIMIT, -PI * 0.2)
|
|
joint.set_param_y(Generic6DOFJoint3D.PARAM_ANGULAR_UPPER_LIMIT, PI * 0.2)
|
|
|
|
joint.set_flag_z(Generic6DOFJoint3D.FLAG_ENABLE_ANGULAR_LIMIT, true)
|
|
joint.set_param_z(Generic6DOFJoint3D.PARAM_ANGULAR_LOWER_LIMIT, -PI * 0.2)
|
|
joint.set_param_z(Generic6DOFJoint3D.PARAM_ANGULAR_UPPER_LIMIT, PI * 0.2)
|
|
|
|
func _create_hinge(node_a: RigidBody3D, node_b: RigidBody3D, pos: Vector3, lower_lim: float, upper_lim: float) -> void:
|
|
var joint = Generic6DOFJoint3D.new()
|
|
add_child(joint)
|
|
joint.position = pos
|
|
joint.node_a = node_a.get_path()
|
|
joint.node_b = node_b.get_path()
|
|
|
|
joint.set_flag_x(Generic6DOFJoint3D.FLAG_ENABLE_LINEAR_LIMIT, true)
|
|
joint.set_flag_y(Generic6DOFJoint3D.FLAG_ENABLE_LINEAR_LIMIT, true)
|
|
joint.set_flag_z(Generic6DOFJoint3D.FLAG_ENABLE_LINEAR_LIMIT, true)
|
|
|
|
# Allow X rotation for the hinge (knee/elbow)
|
|
joint.set_flag_x(Generic6DOFJoint3D.FLAG_ENABLE_ANGULAR_LIMIT, true)
|
|
joint.set_param_x(Generic6DOFJoint3D.PARAM_ANGULAR_LOWER_LIMIT, lower_lim)
|
|
joint.set_param_x(Generic6DOFJoint3D.PARAM_ANGULAR_UPPER_LIMIT, upper_lim)
|
|
|
|
# Lock Y and Z
|
|
joint.set_flag_y(Generic6DOFJoint3D.FLAG_ENABLE_ANGULAR_LIMIT, true)
|
|
joint.set_param_y(Generic6DOFJoint3D.PARAM_ANGULAR_LOWER_LIMIT, 0.0)
|
|
joint.set_param_y(Generic6DOFJoint3D.PARAM_ANGULAR_UPPER_LIMIT, 0.0)
|
|
|
|
joint.set_flag_z(Generic6DOFJoint3D.FLAG_ENABLE_ANGULAR_LIMIT, true)
|
|
joint.set_param_z(Generic6DOFJoint3D.PARAM_ANGULAR_LOWER_LIMIT, 0.0)
|
|
joint.set_param_z(Generic6DOFJoint3D.PARAM_ANGULAR_UPPER_LIMIT, 0.0)
|