Files
stickman/scripts/master_rig_builder.gd
T
ryan 28e8798021 Implement Phase 6 features for Stickman Editor
- Updated BUGS.md with new issues related to selection bounding box, touchpad panning speed, and zooming behavior.
- Enhanced PROJECT.md with detailed specifications for touchpad controls, recent colors in the color picker, and a status bar for project information.
- Modified project.godot to update compatibility version to 4.7.
- Added a status bar in stickman_editor.tscn to display cursor coordinates and snap status.
- Updated body_part_panel.gd to handle recent colors and cursor detection over drawing areas.
- Enhanced stickman_editor.gd to manage cursor coordinates display and recent colors tracking.
- Improved whole_stickman_preview.gd to render selection gizmos on top of other parts.
- Created master_rig.tscn for the stickman rig structure with bones and IK targets.
- Developed master_rig_builder.gd to automate the rig building process with remote transforms and IK modifications.
2026-08-14 21:49:06 -04:00

208 lines
7.5 KiB
GDScript

# res://scripts/rig_builder.gd
@tool
extends Node2D
## Builder script mirroring the exact bone layout, RemoteTransform2D links,
## and IK modifications from the source scene.
@export var build_trigger: bool = false:
set(val):
if val:
build_rig()
build_trigger = false
func build_rig() -> void:
for child in get_children():
child.queue_free()
name = "Node2D"
# 1. Main Hierarchy Containers
var sticky := Node2D.new()
sticky.name = "Sticky"
add_child(sticky)
var stickman := Node2D.new()
stickman.name = "Stickman"
sticky.add_child(stickman)
# Container for visual nodes (Line2D / drawn elements)
var body := Node2D.new()
body.name = "Body"
stickman.add_child(body)
# Container for skeleton
var bones := Node2D.new()
bones.name = "Bones"
bones.visible = false
stickman.add_child(bones)
var skeleton := Skeleton2D.new()
skeleton.name = "Skeleton2D"
bones.add_child(skeleton)
# --- 2. BONE HIERARCHY & REMOTE TRANSFORMS ---
# Hip (Root Bone)
var hip := Bone2D.new()
hip.name = "Hip"
hip.position = Vector2(-1, 27)
hip.rest = Transform2D(0.0, Vector2(0, 32))
skeleton.add_child(hip)
# Hip drives main Body visual
_create_remote_transform("HipTransform", hip, "../../../../Body/Body", Vector2(0, -24))
# Left Leg
var leg_u_l := _create_bone("Leg_Upper_Left", Vector2(-1, 1), hip)
_create_remote_transform("RemoteTransform2D", leg_u_l, "../../../../../Body/Leg_Upper_Left", Vector2(1, 0), 2.35619)
var leg_l_l := _create_bone("Leg_Lower_Left", Vector2(-8, 8), leg_u_l)
leg_l_l.auto_calculate_length_and_angle = false
leg_l_l.length = 10.425
leg_l_l.bone_angle = 89.9999
_create_remote_transform("RemoteTransform2D", leg_l_l, "../../../../../../Body/Leg_Lower_Left", Vector2.ZERO, 1.57079)
# Right Leg
var leg_u_r := _create_bone("Leg_Upper_Right", Vector2(2, 1), hip)
_create_remote_transform("RemoteTransform2D", leg_u_r, "../../../../../Body/Leg_Upper_Right", Vector2(-2, 0), 0.785397)
var leg_l_r := _create_bone("Leg_Lower_Right", Vector2(7, 8), leg_u_r)
leg_l_r.auto_calculate_length_and_angle = false
leg_l_r.length = 10.425
leg_l_r.bone_angle = 89.9999
_create_remote_transform("RemoteTransform2D", leg_l_r, "../../../../../../Body/Leg_Lower_Right", Vector2.ZERO, 1.57079)
# Left Arm
var arm_u_l := _create_bone("Arm_Upper_Left", Vector2(-2, -17), hip)
_create_remote_transform("RemoteTransform2D", arm_u_l, "../../../../../Body/Arm_Upper_Left", Vector2(-11, 0))
var arm_l_l := _create_bone("Arm_Lower_Left", Vector2(-11, 0), arm_u_l)
arm_l_l.auto_calculate_length_and_angle = false
arm_l_l.length = 10.0
arm_l_l.bone_angle = -180.0
_create_remote_transform("RemoteTransform2D", arm_l_l, "../../../../../../Body/Arm_Lower_Left", Vector2(-10, 0))
# Right Arm
var arm_u_r := _create_bone("Arm_Upper_Right", Vector2(2, -17), hip)
_create_remote_transform("RemoteTransform2D", arm_u_r, "../../../../../Body/Arm_Upper_Right", Vector2.ZERO)
var arm_l_r := _create_bone("Arm_Lower_Right", Vector2(11, 0), arm_u_r)
arm_l_r.auto_calculate_length_and_angle = false
arm_l_r.length = 10.0
arm_l_r.bone_angle = 0.0
_create_remote_transform("RemoteTransform2D", arm_l_r, "../../../../../../Body/Arm_Lower_Right", Vector2.ZERO)
# Head
var head := _create_bone("Head", Vector2(0, -23), hip)
head.rotation = 0.0713012
head.rest = Transform2D(
Vector2(2.22127e-06, -1.0), # X-axis basis
Vector2(1.0, 2.22127e-06), # Y-axis basis
Vector2(0.0, -23.0) # Origin position
)
_create_remote_transform("RemoteTransform2D", head, "../../../../../Body/Head", Vector2(-0.498689, -6.98226))
# --- 3. IK TARGET CONTAINERS ---
var ik_targets := Node2D.new()
ik_targets.name = "IK Targets"
sticky.add_child(ik_targets)
_create_target("Arm_Left", Vector2(0, 30), ik_targets)
_create_target("Arm_Right", Vector2(4, 29), ik_targets)
_create_target("Leg_Left", Vector2(-1, 50), ik_targets)
_create_target("Leg_Right", Vector2(0, 51), ik_targets)
_create_target("Head", Vector2(0, -10), ik_targets)
# Body target uses RemoteTransform2D to move Hip bone position
var body_target := _create_target("Body", Vector2(-3, -4), ik_targets)
_create_remote_transform("RemoteTransform2D", body_target, "../../../Stickman/Bones/Skeleton2D/Hip", Vector2(2, 31))
# --- 4. MODIFICATION STACK & SOLVERS ---
var mod_stack := SkeletonModificationStack2D.new()
# Leg Left 2-Bone IK
var leg_left_ik := SkeletonModification2DTwoBoneIK.new()
leg_left_ik.target_nodepath = NodePath("../../../IK Targets/Leg_Left")
leg_left_ik.joint_one_bone_idx = 1
leg_left_ik.joint_one_bone2d_node = NodePath("Hip/Leg_Upper_Left")
leg_left_ik.joint_two_bone_idx = 2
leg_left_ik.joint_two_bone2d_node = NodePath("Hip/Leg_Upper_Left/Leg_Lower_Left")
# Leg Right 2-Bone IK
var leg_right_ik := SkeletonModification2DTwoBoneIK.new()
leg_right_ik.target_nodepath = NodePath("../../../IK Targets/Leg_Right")
leg_right_ik.joint_one_bone_idx = 3
leg_right_ik.joint_one_bone2d_node = NodePath("Hip/Leg_Upper_Right")
leg_right_ik.joint_two_bone_idx = 4
leg_right_ik.joint_two_bone2d_node = NodePath("Hip/Leg_Upper_Right/Leg_Lower_Right")
# Arm Right 2-Bone IK
var arm_right_ik := SkeletonModification2DTwoBoneIK.new()
arm_right_ik.target_nodepath = NodePath("../../../IK Targets/Arm_Right")
arm_right_ik.flip_bend_direction = true
arm_right_ik.joint_one_bone_idx = 7
arm_right_ik.joint_one_bone2d_node = NodePath("Hip/Arm_Upper_Right")
arm_right_ik.joint_two_bone_idx = 8
arm_right_ik.joint_two_bone2d_node = NodePath("Hip/Arm_Upper_Right/Arm_Lower_Right")
# Arm Left 2-Bone IK
var arm_left_ik := SkeletonModification2DTwoBoneIK.new()
arm_left_ik.target_nodepath = NodePath("../../../IK Targets/Arm_Left")
arm_left_ik.flip_bend_direction = true
arm_left_ik.joint_one_bone_idx = 5
arm_left_ik.joint_one_bone2d_node = NodePath("Hip/Arm_Upper_Left")
arm_left_ik.joint_two_bone_idx = 6
arm_left_ik.joint_two_bone2d_node = NodePath("Hip/Arm_Upper_Left/Arm_Lower_Left")
# Head LookAt Modifier
var head_lookat := SkeletonModification2DLookAt.new()
head_lookat.bone_index = 9
head_lookat.bone2d_node = NodePath("Hip/Head")
head_lookat.target_nodepath = NodePath("../../../IK Targets/Head")
head_lookat.enable_constraint = true
head_lookat.constraint_angle_min = 65.0
head_lookat.constraint_angle_max = 295.0
head_lookat.constraint_angle_invert = true
head_lookat.constraint_in_localspace = true
mod_stack.add_modification(leg_left_ik)
mod_stack.add_modification(leg_right_ik)
mod_stack.add_modification(arm_right_ik)
mod_stack.add_modification(arm_left_ik)
mod_stack.add_modification(head_lookat)
skeleton.set_modification_stack(mod_stack)
mod_stack.enabled = true
if Engine.is_editor_hint():
_set_owners(self, get_tree().edited_scene_root)
func _create_bone(b_name: String, pos: Vector2, parent_node: Node) -> Bone2D:
var bone := Bone2D.new()
bone.name = b_name
bone.position = pos
bone.rest = bone.transform
parent_node.add_child(bone)
return bone
func _create_target(t_name: String, pos: Vector2, parent_node: Node) -> Node2D:
var target := Node2D.new()
target.name = t_name
target.position = pos
parent_node.add_child(target)
return target
func _create_remote_transform(rt_name: String, parent_node: Node, target_path: String, pos: Vector2 = Vector2.ZERO, rot: float = 0.0) -> RemoteTransform2D:
var rt := RemoteTransform2D.new()
rt.name = rt_name
rt.position = pos
rt.rotation = rot
rt.remote_path = NodePath(target_path)
parent_node.add_child(rt)
return rt
func _set_owners(node: Node, scene_root: Node) -> void:
for child in node.get_children():
child.owner = scene_root
_set_owners(child, scene_root)