Esqueleto 2D

Un brazo articulado hecho de un solo Polygon2D con skin en dos huesos. Girar un Bone2D mueve los vértices que tienen peso en él.

El editor de arriba está ejecutando este proyecto. Cambia una línea y se recarga.

Clases de Godot usadas

Skeleton2DBone2DPolygon2D

El código

scripts/main.gd
extends Node

const UPPER: float = 200.0
const FORE: float = 180.0
const WIDTH: float = 58.0
const COLUMNS: int = 14

var arm_polygon: PackedVector2Array


func _ready() -> void:
	_add_background()
	_add_label('one Polygon2D, two Bone2D, no sprite', Vector2(40, 40))

	var view: Vector2 = get_viewport().get_visible_rect().size

	var rig: Node2D = Node2D.new()
	rig.position = Vector2(view.x * 0.36, view.y * 0.62)
	add_child(rig)

	var skeleton: Skeleton2D = Skeleton2D.new()
	skeleton.name = 'Skeleton'
	rig.add_child(skeleton)

	var upper: Bone2D = Bone2D.new()
	upper.name = 'Upper'
	skeleton.add_child(upper)
	upper.rest = upper.transform

	var fore: Bone2D = Bone2D.new()
	fore.name = 'Fore'
	fore.position = Vector2(UPPER, 0)
	upper.add_child(fore)
	fore.rest = fore.transform

	arm_polygon = _make_strip()

	var arm: Polygon2D = Polygon2D.new()
	arm.polygon = arm_polygon
	arm.polygons = _make_quads()
	arm.color = Color('#8da5f3')
	rig.add_child(arm)
	arm.skeleton = arm.get_path_to(skeleton)
	arm.add_bone(NodePath('Upper'), _weights(false))
	arm.add_bone(NodePath('Upper/Fore'), _weights(true))

	_add_joint(upper, Vector2.ZERO, 20.0, Color('#f3d48d'))
	_add_joint(fore, Vector2.ZERO, 16.0, Color('#f3d48d'))
	_add_joint(fore, Vector2(FORE, 0), 22.0, Color('#8eef97'))

	var swing: Tween = create_tween().set_loops()
	swing.tween_property(upper, 'rotation', deg_to_rad(-55), 0.9).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
	swing.tween_property(upper, 'rotation', deg_to_rad(20), 0.9).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)

	var bend: Tween = create_tween().set_loops()
	bend.tween_property(fore, 'rotation', deg_to_rad(-115), 0.6).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN_OUT)
	bend.tween_property(fore, 'rotation', deg_to_rad(-5), 0.6).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN_OUT)

	print('the arm is a single polygon, the bones move its vertices')


func _make_strip() -> PackedVector2Array:
	var top: PackedVector2Array = PackedVector2Array()
	var bottom: PackedVector2Array = PackedVector2Array()
	var total: float = UPPER + FORE

	for i in COLUMNS + 1:
		var x: float = total * i / COLUMNS
		top.append(Vector2(x, -WIDTH * 0.5))
		bottom.append(Vector2(x, WIDTH * 0.5))

	bottom.reverse()

	return top + bottom


# ear clipping on a strip of collinear points makes triangles that span the elbow
func _make_quads() -> Array:
	var quads: Array = []

	for i in COLUMNS:
		var bottom_index: int = COLUMNS + 1 + (COLUMNS - i)
		quads.append(PackedInt32Array([i, i + 1, bottom_index - 1, bottom_index]))

	return quads


func _weights(distal: bool) -> PackedFloat32Array:
	var weights: PackedFloat32Array = PackedFloat32Array()

	for point in arm_polygon:
		var blend: float = clampf((point.x - UPPER + 40.0) / 80.0, 0.0, 1.0)
		weights.append(blend if distal else 1.0 - blend)

	return weights


func _add_joint(bone: Bone2D, at: Vector2, radius: float, color: Color) -> void:
	var dot: Polygon2D = Polygon2D.new()
	dot.polygon = _make_circle(radius)
	dot.color = color
	dot.position = at
	dot.z_index = 1
	bone.add_child(dot)


func _make_circle(radius: float) -> PackedVector2Array:
	var points: PackedVector2Array = PackedVector2Array()

	for i in 24:
		var angle: float = TAU * i / 24
		points.append(Vector2(cos(angle), sin(angle)) * radius)

	return points


func _add_background() -> void:
	var background: ColorRect = ColorRect.new()
	background.color = Color('#12141a')
	add_child(background)
	background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)


func _add_label(text: String, at: Vector2) -> void:
	var label: Label = Label.new()
	label.text = text
	label.position = at
	label.add_theme_color_override('font_color', Color('#999999'))
	add_child(label)

Más ejemplos de Animación