动画进阶
概述
学习目标:
- 掌握动画图的创建和使用
- 理解动画混合的工作原理
- 学会使用动画事件
- 了解动画遮罩的使用
- 掌握缓动函数的使用
前置知识要求:
- 动画基础
- ECS 基础
- 3D 开发基础
核心概念
动画图(Animation Graph)
动画图是 Bevy 中用于组织和管理动画的图形结构。它允许你创建复杂的动画混合和过渡。
为什么需要动画图?
- 动画混合:可以在多个动画之间进行平滑过渡
- 动画组织:可以更好地组织和管理复杂的动画
- 性能优化:可以优化动画播放的性能
- 灵活性:可以动态调整动画权重
动画事件(Animation Events)
动画事件允许你在动画播放的特定时间点触发自定义事件。
为什么需要动画事件?
- 同步:可以在动画播放时同步其他系统
- 交互:可以在动画播放时触发交互
- 反馈:可以在动画播放时提供反馈
- 控制:可以更好地控制动画播放流程
动画遮罩(Animation Masks)
动画遮罩允许你限制动画的作用范围,只对特定的骨骼或目标应用动画。
为什么需要动画遮罩?
- 局部动画:可以对特定部位应用动画
- 动画组合:可以组合多个局部动画
- 性能优化:可以减少不必要的动画计算
- 灵活性:可以更灵活地控制动画
缓动函数(Easing Functions)
缓动函数控制动画的加速和减速曲线,使动画更加自然。
为什么需要缓动函数?
- 自然感:可以使动画更加自然
- 视觉效果:可以增强视觉效果
- 用户体验:可以改善用户体验
- 艺术表现:可以增强艺术表现力
基础用法
动画图
创建和使用动画图。
源代码文件:bevy/examples/animation/animation_graph.rs
代码示例:
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut graphs: ResMut<Assets<AnimationGraph>>,
) {
// 加载动画片段
let clip_handle = asset_server.load("animations/fox_idle.gltf#Animation0");
// 从动画片段创建动画图
let (graph, node_index) = AnimationGraph::from_clip(clip_handle);
let graph_handle = graphs.add(graph);
// 创建动画播放器
let mut player = AnimationPlayer::default();
player.play(node_index).repeat();
// 创建实体并添加动画组件
commands.spawn((
AnimationGraphHandle(graph_handle),
player,
// ... 其他组件
));
}关键要点:
- 动画图用于组织和管理动画
- 可以从动画片段创建动画图
- 动画播放器用于播放动画图中的节点
- 可以设置动画的重复模式
说明: 动画图是 Bevy 动画系统的核心。它允许你创建复杂的动画混合和过渡,使动画更加灵活和强大。
动画混合
在多个动画之间进行混合。
源代码文件:bevy/examples/animation/animation_graph.rs
代码示例:
use bevy::prelude::*;
fn setup_animation_graph(
mut graphs: ResMut<Assets<AnimationGraph>>,
mut clips: ResMut<Assets<AnimationClip>>,
asset_server: Res<AssetServer>,
) {
// 加载多个动画片段
let idle_clip = asset_server.load("animations/fox_idle.gltf#Animation0");
let walk_clip = asset_server.load("animations/fox_walk.gltf#Animation0");
let run_clip = asset_server.load("animations/fox_run.gltf#Animation0");
// 创建动画图
let mut graph = AnimationGraph::default();
// 添加动画节点
let idle_node = graph.add_clip(idle_clip);
let walk_node = graph.add_clip(walk_clip);
let run_node = graph.add_clip(run_clip);
// 创建混合节点
let blend_node = graph.add_blend("Root");
// 连接节点
graph.connect(idle_node, blend_node);
graph.connect(walk_node, blend_node);
graph.connect(run_node, blend_node);
// 设置混合权重
graph.set_blend_weight(blend_node, idle_node, 0.5);
graph.set_blend_weight(blend_node, walk_node, 0.3);
graph.set_blend_weight(blend_node, run_node, 0.2);
let graph_handle = graphs.add(graph);
// 使用动画图
// ...
}关键要点:
- 可以在多个动画之间进行混合
- 可以设置混合权重来控制混合比例
- 混合节点用于组合多个动画
- 可以动态调整混合权重
说明: 动画混合是创建平滑动画过渡的关键。通过混合多个动画,可以创建更加自然和流畅的动画效果。
动画事件
在动画播放时触发自定义事件。
源代码文件:bevy/examples/animation/animation_events.rs
代码示例:
use bevy::prelude::*;
#[derive(AnimationEvent, Clone)]
struct SetMessage {
value: String,
color: Color,
}
fn setup(
mut commands: Commands,
mut animations: ResMut<Assets<AnimationClip>>,
mut graphs: ResMut<Assets<AnimationGraph>>,
) {
// 创建动画片段
let mut animation = AnimationClip::default();
animation.set_duration(2.0);
// 添加事件
animation.add_event(
0.0,
SetMessage {
value: "HELLO".into(),
color: Color::srgb(0.0, 0.5, 1.0),
},
);
animation.add_event(
1.0,
SetMessage {
value: "BYE".into(),
color: Color::srgb(1.0, 0.0, 0.0),
},
);
// 创建动画图
let (graph, animation_index) = AnimationGraph::from_clip(animations.add(animation));
let mut player = AnimationPlayer::default();
player.play(animation_index).repeat();
commands.spawn((
AnimationGraphHandle(graphs.add(graph)),
player,
));
}
// 监听动画事件
fn on_set_message(
set_message: On<SetMessage>,
text: Single<(&mut Text2d, &mut TextColor), With<MessageText>>,
) {
let (mut text, mut color) = text.into_inner();
text.0 = set_message.value.clone();
color.0 = set_message.color;
}关键要点:
- 动画事件可以在动画播放的特定时间点触发
- 需要实现
AnimationEventtrait - 可以使用
On<T>观察者来监听动画事件 - 事件可以携带自定义数据
说明: 动画事件是同步动画和其他系统的重要机制。通过动画事件,可以在动画播放时触发自定义逻辑,使动画更加交互和动态。
动画遮罩
限制动画的作用范围。
源代码文件:bevy/examples/animation/animation_masks.rs
代码示例:
use bevy::prelude::*;
use std::collections::HashSet;
// 定义遮罩组
const MASK_GROUP_HEAD: u32 = 0;
const MASK_GROUP_LEFT_ARM: u32 = 1;
const MASK_GROUP_RIGHT_ARM: u32 = 2;
fn setup_animation_with_mask(
mut commands: Commands,
mut graphs: ResMut<Assets<AnimationGraph>>,
asset_server: Res<AssetServer>,
) {
// 加载动画
let clip_handle = asset_server.load("animations/character_walk.gltf#Animation0");
// 创建动画图
let (graph, node_index) = AnimationGraph::from_clip(clip_handle);
// 创建遮罩组
let mut mask_group = HashSet::new();
mask_group.insert(AnimationTargetId::from_name(&Name::new("Head")));
mask_group.insert(AnimationTargetId::from_name(&Name::new("Neck")));
// 应用遮罩
graph.set_mask(node_index, MASK_GROUP_HEAD, mask_group);
let graph_handle = graphs.add(graph);
let mut player = AnimationPlayer::default();
player.play(node_index).repeat();
commands.spawn((
AnimationGraphHandle(graph_handle),
player,
));
}关键要点:
- 动画遮罩可以限制动画的作用范围
- 可以创建多个遮罩组
- 遮罩组由动画目标 ID 集合定义
- 可以动态启用或禁用遮罩组
说明: 动画遮罩是创建局部动画的重要工具。通过遮罩,可以对特定部位应用动画,而不会影响其他部位,使动画更加灵活和精细。
缓动函数
使用缓动函数控制动画曲线。
源代码文件:bevy/examples/animation/easing_functions.rs
代码示例:
use bevy::prelude::*;
fn setup_animation_with_easing(
mut commands: Commands,
mut animations: ResMut<Assets<AnimationClip>>,
mut graphs: ResMut<Assets<AnimationGraph>>,
) {
// 创建动画片段
let mut animation = AnimationClip::default();
// 创建缓动曲线
let ease_function = EaseFunction::CubicInOut;
// 添加动画曲线(使用缓动函数)
animation.add_curve_to_target(
target_id,
AnimatableCurve::new(
animated_field!(Transform::translation),
AnimatableKeyframeCurve::new(
[0.0, 1.0, 2.0].into_iter().zip([
Vec3::ZERO,
Vec3::new(5.0, 0.0, 0.0),
Vec3::ZERO,
]),
)
.with_easing(ease_function)
.expect("valid curve"),
),
);
let (graph, node_index) = AnimationGraph::from_clip(animations.add(animation));
let mut player = AnimationPlayer::default();
player.play(node_index).repeat();
commands.spawn((
AnimationGraphHandle(graphs.add(graph)),
player,
));
}关键要点:
- 缓动函数控制动画的加速和减速曲线
- Bevy 提供了多种内置缓动函数
- 可以在动画曲线上应用缓动函数
- 缓动函数使动画更加自然
说明: 缓动函数是创建自然动画的关键。通过使用不同的缓动函数,可以创建各种动画效果,从线性到弹性,从平滑到弹跳。
进阶用法
动态调整动画权重
在运行时动态调整动画混合权重。
源代码文件:bevy/examples/animation/animation_graph.rs
关键信息:
- 可以在运行时动态调整混合权重
- 权重调整会平滑过渡
- 可以基于游戏状态调整权重
- 权重总和应该为 1.0
说明: 动态调整动画权重是创建响应式动画的关键。通过根据游戏状态调整权重,可以创建更加动态和交互的动画效果。
动画图序列化
将动画图序列化为文件。
源代码文件:bevy/examples/animation/animation_graph.rs
关键信息:
- 动画图可以序列化为 RON 格式
- 可以从文件加载动画图
- 序列化可以保存复杂的动画配置
- 可以编辑序列化的动画图
说明: 动画图序列化是保存和加载动画配置的重要功能。通过序列化,可以保存复杂的动画配置,并在需要时加载它们。
实际应用
在游戏开发中的应用场景
动画进阶功能在游戏开发中有广泛的应用:
- 角色动画:使用动画混合创建平滑的角色动画过渡
- 交互反馈:使用动画事件触发交互反馈
- 局部动画:使用动画遮罩创建局部动画效果
- UI 动画:使用缓动函数创建自然的 UI 动画
常见问题
问题 1:如何创建平滑的动画过渡?
解决方案:使用动画混合和缓动函数。通过混合多个动画并应用缓动函数,可以创建平滑的动画过渡。
问题 2:如何在动画播放时触发其他系统?
解决方案:使用动画事件。通过定义自定义动画事件并在动画播放时触发它们,可以同步动画和其他系统。
问题 3:如何只对特定部位应用动画?
解决方案:使用动画遮罩。通过创建遮罩组并应用它们,可以限制动画的作用范围。
性能考虑
- 动画图优化:尽量减少动画图中的节点数量
- 遮罩优化:只对需要的部位应用遮罩
- 事件优化:避免在动画事件中执行耗时操作
- 缓动函数优化:选择简单的缓动函数以提高性能
相关资源
相关源代码文件:
bevy/examples/animation/animation_graph.rs- 动画图示例bevy/examples/animation/animation_events.rs- 动画事件示例bevy/examples/animation/animation_masks.rs- 动画遮罩示例bevy/examples/animation/easing_functions.rs- 缓动函数示例
官方文档链接:
进一步学习建议:
- 学习 UI 动画,了解如何对 UI 元素应用动画
- 学习变形目标,了解如何使用变形目标动画
- 学习 3D 图形,了解如何将动画应用于 3D 模型
索引:返回上级目录