Basic Bone For Flag Animation

 Phase 1



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")




print("==========================================")







---------------------------
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("==========================================")

TRANSLATE this Page

Posting Komentar

0 Komentar