mcpField
B
blender-animation

blender-animation

github.com/roble3创意与媒体1281.4k 次安装2 周前更新
代码仓库

关于 blender-animation

blender-animation 是一项 Blender 技能,介绍如何用关键帧、曲线、形态键、驱动器和 NLA 条带制作物体与角色动画,并提供自然运动和机械运动的配方及排错说明。

主要功能

  1. 01位置与旋转关键帧
  2. 02插值与缓动控制
  3. 03用于变形和音素的形态键动画
  4. 04属性驱动器
  5. 05可复用的 NLA 条带
  6. 06兼容 Blender 4.x 与 5.x 的 F 曲线

适用场景

  1. 01制作物体移动或旋转动画
  2. 02创建匀速机械运动
  3. 03制作循环待机动画
  4. 04为口型同步制作面部音素动画

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/roble3/cc-blender-skill --skill blender-animation

安装后在 Claude Code 中输入/blender-animation确认已加载。

附带文件evals/evals.jsonreferences/overview.md

详细介绍

Blender 动画

随时间为属性添加动画。大多数动画都只需要关键帧——诀窍在于根据动作的特点选择合适的插值和缓动。

决策树

What kind of motion?
├── Object movement (translate / rotate / scale)
│   → Keyframe `location` / `rotation_euler` / `scale`
│   → Recipe 1, 2
│
├── Mechanical / constant speed (gears, conveyor belts, scrolling)
│   → Linear interpolation
│   → Recipe 3
│
├── Natural / organic (most things)
│   → Bezier interpolation with auto handles
│   → Recipe 1
│
├── Cartoon / stylized (overshoot, bounce, anticipation)
│   → Bounce / Elastic / Back easing
│   → Recipe 4
│
├── Facial / morph / blendshape
│   → Shape Keys, animate `value` property
│   → Recipe 5
│
├── Mechanical relations (one property = function of another)
│   → Drivers (Python expression)
│   → Recipe 6
│
└── Reusable / layered animations
    → NLA actions
    → Recipe 7

配方

配方 1 — 为物体位置添加动画(贝塞尔,自然)
import bpy

obj = bpy.data.objects['GEO-target']

scene = bpy.context.scene
scene.frame_start = 1
scene.frame_end = 60
scene.render.fps = 24

# Keyframe 1: at frame 1, at origin
scene.frame_set(1)
obj.location = (0, 0, 0)
obj.keyframe_insert('location', frame=1)

# Keyframe 2: at frame 60, moved to (5, 0, 0)
scene.frame_set(60)
obj.location = (5, 0, 0)
obj.keyframe_insert('location', frame=60)

print(f"animated:{obj.name} 1->60")

默认插值 = 贝塞尔(平滑进入/退出)。要改为线性,请参阅配方 3。

配方 2 — 为旋转添加动画(360° 自转)
import bpy, math

obj = bpy.data.objects['GEO-target']
scene = bpy.context.scene

# Use rotation_euler with a single axis.
# WARNING: animating past 180° on Euler can flip; use multiple keyframes or quaternions for full rotations.

scene.frame_set(1)
obj.rotation_euler = (0, 0, 0)
obj.keyframe_insert('rotation_euler', frame=1)

scene.frame_set(120)
obj.rotation_euler = (0, 0, math.radians(180))   # half-turn
obj.keyframe_insert('rotation_euler', frame=120)

scene.frame_set(240)
obj.rotation_euler = (0, 0, math.radians(360))   # full turn
obj.keyframe_insert('rotation_euler', frame=240)

print(f"rotated:{obj.name} 360 over 240 frames")

要实现完全恒定的自转,请将关键帧设为线性插值(配方 3)。

配方 3 — 将关键帧设为线性插值

⚠ Blender 5.x 更改了 Action API。 已移除旧版 action.fcurves,改用分层 Actions:action.layers[].strips[].channelbags[].fcurves。请使用此兼容辅助函数。

import bpy

def get_fcurves_compat(action):
    """Return all fcurves on an Action — works on both legacy (≤4.x) and layered (5.x+) actions."""
    if hasattr(action, 'fcurves'):
        return list(action.fcurves)
    fcurves = []
    for layer in action.layers:
        for strip in layer.strips:
            if hasattr(strip, 'channelbags'):
                for cb in strip.channelbags:
                    fcurves.extend(cb.fcurves)
    return fcurves

obj = bpy.data.objects['GEO-target']
if obj.animation_data and obj.animation_data.action:
    for fc in get_fcurves_compat(obj.animation_data.action):
        for kp in fc.keyframe_points:
            kp.interpolation = 'LINEAR'
print(f"interp:linear {obj.name}")

其他选项:'BEZIER'(默认)、'CONSTANT'(阶跃)、'SINE'、'QUAD'、'CUBIC'、'QUART'、'QUINT'、'BOUNCE'、'ELASTIC'、'BACK'。

配方 4 — 为特定关键帧添加弹跳/卡通缓动
import bpy

# (Re-use get_fcurves_compat from Recipe 3.)
def get_fcurves_compat(action):
    if hasattr(action, 'fcurves'):
        return list(action.fcurves)
    fcurves = []
    for layer in action.layers:
        for strip in layer.strips:
            if hasattr(strip, 'channelbags'):
                for cb in strip.channelbags:
                    fcurves.extend(cb.fcurves)
    return fcurves

obj = bpy.data.objects['GEO-target']
target_fc = None
for fc in get_fcurves_compat(obj.animation_data.action):
    if fc.data_path == 'location' and fc.array_index == 2:   # Z axis
        target_fc = fc
        break

if target_fc and len(target_fc.keyframe_points) >= 2:
    last_kp = target_fc.keyframe_points[-1]
    last_kp.interpolation = 'BOUNCE'
    last_kp.easing = 'EASE_OUT'    # 'AUTO', 'EASE_IN', 'EASE_OUT', 'EASE_IN_OUT'
print('animated:bouncy_landing')
配方 5 — 形状键(变形/混合变形/视素)
import bpy

mesh_obj = bpy.data.objects['GEO-character_face']

# Add basis (the rest pose)
if mesh_obj.data.shape_keys is None:
    basis = mesh_obj.shape_key_add(name='Basis')

# Add a morph target
smile = mesh_obj.shape_key_add(name='Smile')
smile.value = 0.0
# ⚠ At this point, switch to Edit Mode interactively and modify the mesh while 'Smile' is selected.
#   Or set vertex coordinates programmatically (advanced).

# Animate
scene = bpy.context.scene
scene.frame_set(1)
smile.value = 0.0
smile.keyframe_insert('value', frame=1)

scene.frame_set(24)
smile.value = 1.0
smile.keyframe_insert('value', frame=24)

print('animated:shape_key_smile')

用于口型同步:使用标准的 15 个视素集合(Oculus / ARKit)——将形状键命名为 viseme_aa、viseme_E、viseme_O 等。沿音频时间线为每个视素的值添加动画。

配方 6 — 驱动器(将一个属性设为另一个属性的表达式)
import bpy

# Example: child object's X = parent's X × 2
target = bpy.data.objects['GEO-follower']
source = bpy.data.objects['GEO-leader']

fc = target.driver_add('location', 0)   # X axis
driver = fc.driver
driver.type = 'SCRIPTED'

# Add variable referencing source's X position
var = driver.variables.new()
var.name = 'src_x'
var.type = 'TRANSFORMS'
var.targets[0].id = source
var.targets[0].transform_type = 'LOC_X'
var.targets[0].transform_space = 'WORLD_SPACE'

driver.expression = 'src_x * 2'
print(f"driver:{target.name}.x = {source.name}.x * 2")
配方 7 — 将当前动画推送到 NLA 条带(以便重复使用)
import bpy

obj = bpy.data.objects['GEO-character']

if obj.animation_data and obj.animation_data.action:
    track = obj.animation_data.nla_tracks.new()
    track.name = 'NLA-Walk'
    strip = track.strips.new('Walk', start=1, action=obj.animation_data.action)
    obj.animation_data.action = None    # clear timeline; NLA owns the animation
    print(f"nla:pushed_walk_strip")

完成后,动画即可重复使用——复制条带、缩放时间,并与其他轨道混合。

配方 8 — 微妙的待机动画(可循环旋转)
import bpy, math

obj = bpy.data.objects['GEO-target']

# Tiny sway around Y, 4-second loop
scene = bpy.context.scene
scene.frame_start = 1
scene.frame_end = 96    # 4s at 24fps

scene.frame_set(1)
obj.rotation_euler = (0, math.radians(-2), 0)
obj.keyframe_insert('rotation_euler', frame=1)

scene.frame_set(48)
obj.rotation_euler = (0, math.radians(2), 0)
obj.keyframe_insert('rotation_euler', frame=48)

scene.frame_set(96)
obj.rotation_euler = (0, math.radians(-2), 0)
obj.keyframe_insert('rotation_euler', frame=96)

# Set extrapolation mode to cycle (loop)
def get_fcurves_compat(action):
    if hasattr(action, 'fcurves'):
        return list(action.fcurves)
    fcurves = []
    for layer in action.layers:
        for strip in layer.strips:
            if hasattr(strip, 'channelbags'):
                for cb in strip.channelbags:
                    fcurves.extend(cb.fcurves)
    return fcurves

if obj.animation_data and obj.animation_data.action:
    for fc in get_fcurves_compat(obj.animation_data.action):
        fc.modifiers.new('CYCLES')
print('animated:idle_loop')

专业动画原则

  • 缓入、缓出 — 贝塞尔手柄会自动实现此效果;符合现实世界的物理规律
  • 预备动作 — 主动作之前的小幅反向运动(跳跃前的准备)
  • 跟随动作 — 主动作停止后,次要部分继续运动(披风、头发)
  • 挤压与拉伸 — 使用形状键或缩放动画进行夸张表现
  • 弧线 — 自然运动沿曲线而非直线进行

常见问题

症状解决方法
动作机械默认的贝塞尔插值是正确选择;线性插值不适合有机物体
旋转在 180° 处翻转使用多个关键帧(90、180、270、360)或四元数
形状键更改丢失必须退出编辑模式,才能提交形状键状态
动画只应用于一个轴keyframe_insert('location', index=0) 仅用于 X 轴;index=1 对应 Y 轴,=2 对应 Z 轴
驱动器未更新刷新视口;检查 Preferences → Editing → Allow Driver Python Expression
渲染帧率不匹配请在添加动画前设置 scene.render.fps,以避免时间漂移

何时加载 references/overview.md

在以下情况下加载:

  • 需要微调动画曲线(手柄类型、贝塞尔形状控制)
  • 需要使用 NLA 分层/混合
  • 驱动器涉及复杂表达式(多变量、条件)
  • 需要完整的 ARKit 52 混合变形面部动画
  • 需要步行循环/跑步循环/攻击动作模式

该参考文档涵盖:完整的 F 曲线插值/缓动矩阵、NLA 工作流、驱动器实用指南、形状键最佳实践、动画原则。

评论

支持 Markdown