blender-animation
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBlender Animation
Blender 动画
Animate properties over time. Most animation is just keyframes — the trick is choosing the right interpolation and easing for the motion's character.
随时间为属性制作动画。大多数动画仅依赖关键帧——关键在于为运动特性选择合适的插值与缓动方式。
Decision tree
决策树
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 7What 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 7Recipes
操作示例
Recipe 1 — Animate object position (Bezier, natural)
示例1 — 为物体位置制作动画(Bezier缓动,自然运动)
python
import bpy
obj = bpy.data.objects['GEO-target']
scene = bpy.context.scene
scene.frame_start = 1
scene.frame_end = 60
scene.render.fps = 24python
import bpy
obj = bpy.data.objects['GEO-target']
scene = bpy.context.scene
scene.frame_start = 1
scene.frame_end = 60
scene.render.fps = 24Keyframe 1: at frame 1, at origin
关键帧1:第1帧,位于原点
scene.frame_set(1)
obj.location = (0, 0, 0)
obj.keyframe_insert('location', frame=1)
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)
关键帧2:第60帧,移动至(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")
Default interpolation = Bezier (smooth in/out). To make it linear, see Recipe 3.scene.frame_set(60)
obj.location = (5, 0, 0)
obj.keyframe_insert('location', frame=60)
print(f"animated:{obj.name} 1->60")
默认插值方式为Bezier(平滑入/出)。如需线性插值,请查看示例3。Recipe 2 — Animate rotation (a 360° spin)
示例2 — 为旋转制作动画(360°旋转)
python
import bpy, math
obj = bpy.data.objects['GEO-target']
scene = bpy.context.scenepython
import bpy, math
obj = bpy.data.objects['GEO-target']
scene = bpy.context.sceneUse rotation_euler with a single axis.
使用单轴rotation_euler(欧拉角旋转)。
WARNING: animating past 180° on Euler can flip; use multiple keyframes or quaternions for full rotations.
注意:欧拉角旋转超过180°可能会出现翻转;完整旋转请使用多关键帧或四元数。
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")
For perfectly constant spin, set keyframes to Linear interpolation (Recipe 3).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)) # 半圈
obj.keyframe_insert('rotation_euler', frame=120)
scene.frame_set(240)
obj.rotation_euler = (0, 0, math.radians(360)) # 整圈
obj.keyframe_insert('rotation_euler', frame=240)
print(f"rotated:{obj.name} 360 over 240 frames")
如需完全匀速旋转,请将关键帧设置为线性插值(见示例3)。Recipe 3 — Set keyframes to Linear interpolation
示例3 — 将关键帧设置为线性插值
⚠ Blender 5.x changed the Action API. Legacy was removed in favour of layered Actions: . Use this compat helper.
action.fcurvesaction.layers[].strips[].channelbags[].fcurvespython
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}")Other options: (default), (step), , , , , , , , .
'BEZIER''CONSTANT''SINE''QUAD''CUBIC''QUART''QUINT''BOUNCE''ELASTIC''BACK'⚠ Blender 5.x 改动了Action API。旧版的已被移除,取而代之的是分层Actions:。请使用以下兼容工具函数。
action.fcurvesaction.layers[].strips[].channelbags[].fcurvespython
import bpy
def get_fcurves_compat(action):
"""返回Action中的所有fcurves——兼容旧版(≤4.x)和分层(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'Recipe 4 — Bouncy / cartoon easing on a specific keyframe
示例4 — 为特定关键帧添加弹跳/卡通风格缓动
python
import bpypython
import bpy(Re-use get_fcurves_compat from Recipe 3.)
(复用示例3中的get_fcurves_compat函数。)
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')
undefineddef 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轴
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')
undefinedRecipe 5 — Shape keys (morph / blendshape / viseme)
示例5 — Shape keys(形状键,又称变形/混合形状/口型键)
python
import bpy
mesh_obj = bpy.data.objects['GEO-character_face']python
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')
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
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.
⚠ 此时需切换至编辑模式,选中'Smile'形状后交互式修改网格。
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')
**For lip sync**: standard 15-viseme set (Oculus / ARKit) — name shape keys `viseme_aa`, `viseme_E`, `viseme_O`, etc. Animate each viseme's value across the audio timeline.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`等,并在音频时间轴上为每个口型键的value属性制作动画。Recipe 6 — Driver (one property as expression of another)
示例6 — Driver(驱动,一个属性作为另一个属性的表达式)
python
import bpypython
import bpyExample: child object's X = parent's X × 2
示例:子物体的X坐标 = 父物体的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'
target = bpy.data.objects['GEO-follower']
source = bpy.data.objects['GEO-leader']
fc = target.driver_add('location', 0) # X轴
driver = fc.driver
driver.type = 'SCRIPTED'
Add variable referencing source's X position
添加引用源物体X坐标的变量
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")
undefinedvar = 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")
undefinedRecipe 7 — Push current animation to NLA strip (for reuse)
示例7 — 将当前动画推送至NLA片段(用于复用)
python
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")After this, the animation is reusable — duplicate the strip, scale time, blend with other tracks.
python
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 # 清空时间轴;动画由NLA管理
print(f"nla:pushed_walk_strip")完成此操作后,动画即可复用——可复制片段、缩放时间、与其他轨道混合。
Recipe 8 — Subtle idle animation (loopable rotation)
示例8 — 细微的待机动画(可循环旋转)
python
import bpy, math
obj = bpy.data.objects['GEO-target']python
import bpy, math
obj = bpy.data.objects['GEO-target']Tiny sway around Y, 4-second loop
绕Y轴轻微摆动,4秒循环
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)
scene = bpy.context.scene
scene.frame_start = 1
scene.frame_end = 96 # 24fps下为4秒
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)
设置外插模式为循环(cycle)
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')
undefineddef 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')
undefinedPro animation principles
专业动画原则
- Slow in, slow out — Bezier handles do this automatically; matches real-world physics
- Anticipation — small reverse motion before the main action (jump prep)
- Follow-through — secondary parts continue after main motion stops (cape, hair)
- Squash & stretch — exaggeration with shape keys or scale animation
- Arcs — natural motion follows curves, not straight lines
- 慢入慢出——Bezier手柄可自动实现此效果,符合现实物理规律
- 预备动作——主动作前的小幅反向运动(如跳跃前的下蹲)
- 跟随动作——主动作停止后,次要部件继续运动(如斗篷、头发)
- 挤压与拉伸——通过形状键或缩放动画实现夸张效果
- 弧线运动——自然运动遵循曲线而非直线
Common pitfalls
常见问题
| Symptom | Fix |
|---|---|
| Robotic motion | Default Bezier is correct; Linear is wrong for organic things |
| Rotation flips at 180° | Use multiple keyframes (90, 180, 270, 360) or quaternions |
| Shape key changes lost | Must exit Edit Mode to commit shape key state |
| Animation only on one axis | |
| Driver doesn't update | Refresh viewport; check Preferences → Editing → Allow Driver Python Expression |
| Render fps mismatch | Set |
| 症状 | 解决方法 |
|---|---|
| 机械感运动 | 默认Bezier插值是正确选择;线性插值不适用于有机物体 |
| 旋转在180°处翻转 | 使用多关键帧(90°、180°、270°、360°)或四元数 |
| 形状键修改丢失 | 必须退出编辑模式才能保存形状键状态 |
| 仅单轴有动画 | 仅为X轴添加关键帧使用 |
| 驱动不更新 | 刷新视口;检查偏好设置→编辑→允许驱动Python表达式 |
| 渲染帧率不匹配 | 制作动画前先设置 |
When to load references/overview.md
references/overview.md何时加载references/overview.md
references/overview.mdLoad when:
- Animation curves need fine tuning (handle types, bezier shape control)
- NLA layering / blending needed
- Drivers with complex expressions (multi-variable, conditional)
- ARKit 52-blendshape full face animation
- Walk-cycle / run-cycle / attack patterns
The reference covers: full F-curve interpolation/easing matrix, NLA workflow, drivers cookbook, shape-key best practices, animation principles.
在以下场景加载:
- 需要微调动画曲线(手柄类型、Bezier形状控制)
- 需要NLA分层/混合
- 使用带有复杂表达式的驱动(多变量、条件表达式)
- ARKit 52种混合形状的全脸动画
- 行走循环/奔跑循环/攻击动作模式
该参考文档涵盖:完整的F曲线插值/缓动矩阵、NLA工作流程、驱动使用指南、形状键最佳实践、动画原则。