关于 blender-animation
blender-animation 是一项 Blender 技能,介绍如何用关键帧、曲线、形态键、驱动器和 NLA 条带制作物体与角色动画,并提供自然运动和机械运动的配方及排错说明。
主要功能
- 01位置与旋转关键帧
- 02插值与缓动控制
- 03用于变形和音素的形态键动画
- 04属性驱动器
- 05可复用的 NLA 条带
- 06兼容 Blender 4.x 与 5.x 的 F 曲线
适用场景
- 01制作物体移动或旋转动画
- 02创建匀速机械运动
- 03制作循环待机动画
- 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