import bpy
# ============================================================
# FLAG WAVE - Y LOCATION ONLY
# Armature : Armature.005
# Bones : Bone.1 - Bone.5
#
# Frame 1 = Pose A
# Frame 25 = Pose B
# Frame 50 = Pose A
# Frame 75 = Pose B
# Frame 100 = Pose A
#
# LOOP: 50 frames per cycle
# ============================================================
ARMATURE_NAME = "Armature.005"
arm = bpy.data.objects.get(ARMATURE_NAME)
if arm is None:
raise Exception(f"Armature '{ARMATURE_NAME}' tidak ditemukan.")
# ------------------------------------------------------------
# POSE A
# Pangkal kecil -> ujung bebas besar
# ------------------------------------------------------------
POSE_A = {
"Bone.1": 0.00,
"Bone.2": 0.30,
"Bone.3": -0.60,
"Bone.4": 1.00,
"Bone.5": -1.40,
}
# ------------------------------------------------------------
# POSE B
# Fase berlawanan
# ------------------------------------------------------------
POSE_B = {
"Bone.1": 0.00,
"Bone.2": -0.30,
"Bone.3": 0.60,
"Bone.4": -1.00,
"Bone.5": 1.40,
}
# ------------------------------------------------------------
# KEYFRAME PLAN
# ------------------------------------------------------------
KEYS = {
1: POSE_A,
25: POSE_B,
50: POSE_A,
75: POSE_B,
100: POSE_A,
}
# ============================================================
# APPLY KEYFRAMES
# ============================================================
for frame, pose in KEYS.items():
bpy.context.scene.frame_set(frame)
for bone_name, y_value in pose.items():
bone = arm.pose.bones.get(bone_name)
if bone is None:
print(f"WARNING: {bone_name} tidak ditemukan")
continue
# Hanya ubah Y
bone.location.y = y_value
# Keyframe hanya Y Location
bone.keyframe_insert(
data_path="location",
index=1,
frame=frame
)
# ============================================================
# INTERPOLATION
# Bezier supaya gerakan tidak linear/kaku
# ============================================================
if arm.animation_data and arm.animation_data.action:
action = arm.animation_data.action
for fcurve in action.fcurves:
if 'location' in fcurve.data_path and fcurve.array_index == 1:
for kp in fcurve.keyframe_points:
kp.interpolation = 'BEZIER'
# Handle otomatis untuk transisi smooth
kp.handle_left_type = 'AUTO_CLAMPED'
kp.handle_right_type = 'AUTO_CLAMPED'
# ------------------------------------------------------------
# Timeline
# ------------------------------------------------------------
bpy.context.scene.frame_start = 1
bpy.context.scene.frame_end = 100
bpy.context.scene.frame_set(1)
print("==========================================")
print("FLAG WAVE CREATED")
print("Frame 1 : Pose A")
print("Frame 25 : Pose B")
print("Frame 50 : Pose A")
print("Frame 75 : Pose B")
print("Frame 100 : Pose A")
print("Loop : 50 frames")
---------------------------
import bpy
import math
# ============================================================
# TRAVELING FLAG WAVE - EXPLICIT 1..100 LOOP
# Armature : Armature.005
# Bones : Bone.1 - Bone.5
#
# Keyframes only:
# 1, 25, 50, 75, 100
#
# Frame 1 == Frame 100
# ============================================================
ARMATURE_NAME = "Armature.005"
START_FRAME = 1
END_FRAME = 100
KEY_FRAMES = [1, 25, 50, 75, 100]
arm = bpy.data.objects.get(ARMATURE_NAME)
if arm is None:
raise Exception(f"{ARMATURE_NAME} tidak ditemukan")
# ============================================================
# AMPLITUDE
# makin jauh dari pangkal -> makin besar
# ============================================================
AMPLITUDE = {
"Bone.1": 0.20,
"Bone.2": 0.80,
"Bone.3": 1.10,
"Bone.4": 1.50,
"Bone.5": 1.90,
}
# ============================================================
# PHASE OFFSET
#
# dinyatakan sebagai bagian dari 1 siklus
#
# Bone.1 -> pangkal
# Bone.5 -> ujung
# ============================================================
PHASE_OFFSET = {
"Bone.1": 0.00,
"Bone.2": 0.08,
"Bone.3": 0.16,
"Bone.4": 0.24,
"Bone.5": 0.32,
}
# ============================================================
# START PHASE
# ============================================================
START_PHASE = math.pi / 2.0
# ============================================================
# DELETE OLD Y LOCATION KEYFRAMES
# ============================================================
if arm.animation_data and arm.animation_data.action:
action = arm.animation_data.action
for fc in list(action.fcurves):
if fc.array_index != 1:
continue
for bone_name in AMPLITUDE:
target_path = f'pose.bones["{bone_name}"].location'
if fc.data_path == target_path:
action.fcurves.remove(fc)
break
# ============================================================
# CREATE KEYFRAMES
# ============================================================
for frame in KEY_FRAMES:
bpy.context.scene.frame_set(frame)
# Normalized loop position:
#
# frame 1 = 0.0
# frame 100 = 1.0
#
loop_t = (
(frame - START_FRAME)
/ (END_FRAME - START_FRAME)
)
for bone_name, amp in AMPLITUDE.items():
bone = arm.pose.bones.get(bone_name)
if bone is None:
print(f"WARNING: {bone_name} tidak ditemukan")
continue
phase_offset = PHASE_OFFSET[bone_name]
phase = (
2.0 * math.pi * (loop_t - phase_offset)
+ START_PHASE
)
y = amp * math.sin(phase)
bone.location.y = y
bone.keyframe_insert(
data_path="location",
index=1,
frame=frame
)
print(
f"Frame {frame:3d} | "
f"{bone_name:7s} | "
f"Y = {y:+.3f}"
)
# ============================================================
# FORCE FRAME 100 = FRAME 1 EXACTLY
#
# Floating-point theoretically already gives same result,
# but we explicitly copy it for perfect loop.
# ============================================================
for bone_name in AMPLITUDE:
bone = arm.pose.bones.get(bone_name)
if bone is None:
continue
# Read frame 1
bpy.context.scene.frame_set(START_FRAME)
y_start = bone.location.y
# Set exact same value at frame 100
bpy.context.scene.frame_set(END_FRAME)
bone.location.y = y_start
bone.keyframe_insert(
data_path="location",
index=1,
frame=END_FRAME
)
# ============================================================
# INTERPOLATION
# ============================================================
if arm.animation_data and arm.animation_data.action:
action = arm.animation_data.action
for fc in action.fcurves:
if fc.array_index != 1:
continue
for bone_name in AMPLITUDE:
target_path = f'pose.bones["{bone_name}"].location'
if fc.data_path == target_path:
for kp in fc.keyframe_points:
kp.interpolation = 'BEZIER'
kp.handle_left_type = 'AUTO'
kp.handle_right_type = 'AUTO'
# ============================================================
# TIMELINE
# ============================================================
bpy.context.scene.frame_start = START_FRAME
bpy.context.scene.frame_end = END_FRAME
bpy.context.scene.frame_set(START_FRAME)
print("")
print("==========================================")
print("TRAVELING FLAG WAVE DONE")
print("")
print("LOOP:")
print("Frame 1 == Frame 100")
print("")
print("KEYFRAMES:")
print("1, 25, 50, 75, 100")
print("")
print("AMPLITUDE:")
for bone_name, value in AMPLITUDE.items():
print(f"{bone_name}: {value}")
print("")
print("==========================================")


0 Komentar