fix: use Vector3 euler for rotation tracks, NOT Quaternion

track_insert_key() returns -1 (error) when using Quaternion values
on TYPE_ROTATION (type=1) tracks in Godot 4.2.1.

The correct format is: add_track(1) + track_insert_key(track, time, Vector3(euler))

This applies to both Walk/Run/Jump/Idle animations - all must use
Vector3 euler angles for rotation keyframes.
This commit is contained in:
2026-06-25 12:10:00 -04:00
parent 92ebd6c8d2
commit aa9a957eac
+43 -42
View File
@@ -168,31 +168,31 @@ func _create_walk_anim() -> Animation:
# Add rotation tracks for each bone using type=1 (ROTATION)
_add_rot_track(anim, "LeftUpperLeg", cycle, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(-0.6, 0, 0))},
{"t": cycle * 0.5, "q": Quaternion.from_euler(Vector3(0.6, 0, 0))},
{"t": cycle, "q": Quaternion.from_euler(Vector3(-0.6, 0, 0))}
{"t": 0.0, "euler": Vector3(-0.6, 0, 0)},
{"t": cycle * 0.5, "euler": Vector3(0.6, 0, 0)},
{"t": cycle, "euler": Vector3(-0.6, 0, 0)}
])
_add_rot_track(anim, "RightUpperLeg", cycle, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(0.6, 0, 0))},
{"t": cycle * 0.5, "q": Quaternion.from_euler(Vector3(-0.6, 0, 0))},
{"t": cycle, "q": Quaternion.from_euler(Vector3(0.6, 0, 0))}
{"t": 0.0, "euler": Vector3(0.6, 0, 0)},
{"t": cycle * 0.5, "euler": Vector3(-0.6, 0, 0)},
{"t": cycle, "euler": Vector3(0.6, 0, 0)}
])
_add_rot_track(anim, "LeftUpperArm", cycle, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(-0.4, 0, 0))},
{"t": cycle * 0.5, "q": Quaternion.from_euler(Vector3(0.4, 0, 0))},
{"t": cycle, "q": Quaternion.from_euler(Vector3(-0.4, 0, 0))}
{"t": 0.0, "euler": Vector3(-0.4, 0, 0)},
{"t": cycle * 0.5, "euler": Vector3(0.4, 0, 0)},
{"t": cycle, "euler": Vector3(-0.4, 0, 0)}
])
_add_rot_track(anim, "RightUpperArm", cycle, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(0.4, 0, 0))},
{"t": cycle * 0.5, "q": Quaternion.from_euler(Vector3(-0.4, 0, 0))},
{"t": cycle, "q": Quaternion.from_euler(Vector3(0.4, 0, 0))}
{"t": 0.0, "euler": Vector3(0.4, 0, 0)},
{"t": cycle * 0.5, "euler": Vector3(-0.4, 0, 0)},
{"t": cycle, "euler": Vector3(0.4, 0, 0)}
])
_add_rot_track(anim, "Spine", cycle, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))},
{"t": cycle * 0.25, "q": Quaternion.from_euler(Vector3(0, 0.1, 0))},
{"t": cycle * 0.5, "q": Quaternion.from_euler(Vector3(0, 0, 0))},
{"t": cycle * 0.75, "q": Quaternion.from_euler(Vector3(0, -0.1, 0))},
{"t": cycle, "q": Quaternion.from_euler(Vector3(0, 0, 0))}
{"t": 0.0, "euler": Vector3(0, 0, 0)},
{"t": cycle * 0.25, "euler": Vector3(0, 0.1, 0)},
{"t": cycle * 0.5, "euler": Vector3(0, 0, 0)},
{"t": cycle * 0.75, "euler": Vector3(0, -0.1, 0)},
{"t": cycle, "euler": Vector3(0, 0, 0)}
])
return anim
@@ -207,24 +207,24 @@ func _create_jump_anim() -> Animation:
anim.loop_mode = Animation.LOOP_LINEAR
_add_rot_track(anim, "LeftUpperArm", 1.0, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))},
{"t": 0.5, "q": Quaternion.from_euler(Vector3(-1.2, 0, 0))},
{"t": 1.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))}
{"t": 0.0, "euler": Vector3(0, 0, 0)},
{"t": 0.5, "euler": Vector3(-1.2, 0, 0)},
{"t": 1.0, "euler": Vector3(0, 0, 0)}
])
_add_rot_track(anim, "RightUpperArm", 1.0, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))},
{"t": 0.5, "q": Quaternion.from_euler(Vector3(-1.2, 0, 0))},
{"t": 1.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))}
{"t": 0.0, "euler": Vector3(0, 0, 0)},
{"t": 0.5, "euler": Vector3(-1.2, 0, 0)},
{"t": 1.0, "euler": Vector3(0, 0, 0)}
])
_add_rot_track(anim, "LeftUpperLeg", 1.0, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))},
{"t": 0.5, "q": Quaternion.from_euler(Vector3(-0.3, 0, 0))},
{"t": 1.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))}
{"t": 0.0, "euler": Vector3(0, 0, 0)},
{"t": 0.5, "euler": Vector3(-0.3, 0, 0)},
{"t": 1.0, "euler": Vector3(0, 0, 0)}
])
_add_rot_track(anim, "RightUpperLeg", 1.0, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))},
{"t": 0.5, "q": Quaternion.from_euler(Vector3(-0.3, 0, 0))},
{"t": 1.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))}
{"t": 0.0, "euler": Vector3(0, 0, 0)},
{"t": 0.5, "euler": Vector3(-0.3, 0, 0)},
{"t": 1.0, "euler": Vector3(0, 0, 0)}
])
return anim
@@ -234,24 +234,24 @@ func _create_idle_anim() -> Animation:
anim.loop_mode = Animation.LOOP_LINEAR
_add_rot_track(anim, "Spine", 2.0, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))},
{"t": 1.0, "q": Quaternion.from_euler(Vector3(0.02, 0, 0))},
{"t": 2.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))}
{"t": 0.0, "euler": Vector3(0, 0, 0)},
{"t": 1.0, "euler": Vector3(0.02, 0, 0)},
{"t": 2.0, "euler": Vector3(0, 0, 0)}
])
_add_rot_track(anim, "Head", 2.0, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))},
{"t": 1.0, "q": Quaternion.from_euler(Vector3(0.03, 0.02, 0))},
{"t": 2.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))}
{"t": 0.0, "euler": Vector3(0, 0, 0)},
{"t": 1.0, "euler": Vector3(0.03, 0.02, 0)},
{"t": 2.0, "euler": Vector3(0, 0, 0)}
])
_add_rot_track(anim, "LeftUpperArm", 2.0, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))},
{"t": 1.0, "q": Quaternion.from_euler(Vector3(0.02, 0, 0.05))},
{"t": 2.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))}
{"t": 0.0, "euler": Vector3(0, 0, 0)},
{"t": 1.0, "euler": Vector3(0.02, 0, 0.05)},
{"t": 2.0, "euler": Vector3(0, 0, 0)}
])
_add_rot_track(anim, "RightUpperArm", 2.0, [
{"t": 0.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))},
{"t": 1.0, "q": Quaternion.from_euler(Vector3(0.02, 0, -0.05))},
{"t": 2.0, "q": Quaternion.from_euler(Vector3(0, 0, 0))}
{"t": 0.0, "euler": Vector3(0, 0, 0)},
{"t": 1.0, "euler": Vector3(0.02, 0, -0.05)},
{"t": 2.0, "euler": Vector3(0, 0, 0)}
])
return anim
@@ -260,7 +260,8 @@ func _add_rot_track(anim: Animation, bone_name: String, length: float, keyframes
anim.track_set_path(track, "MikuRig/Skeleton3D:" + bone_name)
anim.track_set_interpolation_type(track, Animation.INTERPOLATION_LINEAR)
for kf in keyframes:
anim.track_insert_key(track, kf["t"], kf["q"])
# Use Vector3 euler angles (NOT Quaternion) - Godot 4.2.1 expects euler Vector3
anim.track_insert_key(track, kf["t"], kf["euler"])
func _is_animation_broken(ap: AnimationPlayer, anim_name: String) -> bool:
## Returns true if the animation has near-zero motion (Blender GLTF track stripping).