Files
aibis-dream/Docs/帧动画角色配置指南(策划版).md
2026-08-05 21:14:06 +08:00

272 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 帧动画角色配置指南(策划版)
本文说明帧动画角色的 **Flow 配置**与 **Yarn 调用**。Clip 的切图、导入和帧时长配置不在本文展开。
示例资源:`Assets/GameContent/Huoshan/Actor/火山Graph.asset`
> 当前的 `火山Graph` 已有 Clip,但还没有配置 Flow。下文的 Flow 名称是教学示例,不代表资源中已经存在。
## 1. 先理解 Clip 和 Flow
- **Clip**:一段独立帧动画,例如 `摘帽``扣头切屏特效``伸手表情idle`
- **Flow**:把多个 Clip 按顺序串成一次完整表演,例如 `摘帽 → 伸手表情idle`
- Yarn 调用时,Clip ID 和 Flow ID 的写法完全相同,系统会自动查找对应内容。
适合直接调用 Clip 的情况:只播放一个动作或切换一个循环表情。
适合配置 Flow 的情况:动作需要连续播放多个阶段,并且希望 Yarn 只写一条命令。
## 2. 打开火山 Graph
1. 在 Project 窗口选中 `Assets/GameContent/Huoshan/Actor/火山Graph.asset`
2. 点击 Inspector 中的 **Open Frame Animation Graph Editor**
3. 也可以从 Unity 菜单打开:**AIBIS > 帧动画 Graph 编辑器**,再选择 `火山Graph`
编辑器左侧是 Clip / Flow 列表,中间是节点画布,右侧是当前选中内容的属性。
## 3. 配置一个 Flow
下面以新建 `摘帽到伸手_Flow` 为例,预期顺序为:
```text
摘帽 → 伸手表情idle(循环)
```
### 第一步:把 Clip 放到画布
1. 在左侧选择 **Clips**
2.`摘帽` 从左侧拖到中间画布,生成一个 Node。
3. 再把 `伸手表情idle` 拖到画布,生成第二个 Node。
同一个 Clip 可以在画布中生成多个 Node。Node 只是 Flow 中对 Clip 的一次引用,不会复制或修改原 Clip。
### 第二步:连接播放顺序
`摘帽` Node 右侧的输出点拖线,连接到 `伸手表情idle` Node 左侧的输入点。
当前 Flow 只支持单线顺序播放:
- 一个 Node 最多只能连接一个后继 Node;
- 不支持分支;
- 不支持把路径连成环;
- 播放顺序由连线决定,不由节点在画布上的左右位置决定。
### 第三步:创建 Flow 并指定入口
1. 选中 `摘帽` Node。
2. 右键该 Node,选择 **Create Flow From Node**;也可用画布上方 **Canvas > Create Flow From Selection**
3. 将 Flow ID 填为 `摘帽到伸手_Flow`
4. 确认入口是 `摘帽` Node。入口 Node 会显示 **E** 标记。
Flow ID 就是 Yarn 中填写的动画名。建议使用有明确含义且不易重复的名称,例如 `摘帽到伸手_Flow`
> Clip ID 和 Flow ID 共用同一套命名空间,不能重名。修改 ID 后,已有 Yarn 文本不会自动更新,必须同步搜索并修改调用。
### 第四步:设置结尾行为
选中最后一个 Node,在右侧设置 **Override End Behavior**。常用选项:
| 选项 | 播放结束后的表现 | 常见用途 |
| --- | --- | --- |
| `Loop` | 从头循环最后一个 Clip | idle、持续表情 |
| `HoldLastFrame` | 停在最后一帧 | 一次动作的定格结尾 |
| `Clear` | 清空当前 Sprite | 动画结束后不显示图片 |
| `HideTarget` | 隐藏渲染目标 | 动画结束后隐藏角色 |
本例最后的 `伸手表情idle` 应使用 `Loop`
注意:只有终点 Node 才能设置结束行为。一个 Node 如果设置了结束行为,就不能再连接后继 Node。中间 Node 播完后会自动进入下一个 Node,不需要设置结束行为。
如需单独调整某个 Node 的速度,可勾选 **Override Speed**`1` 为原速,`2` 为两倍速,`0.5` 为半速。速度必须大于 `0`
### 第五步:预览、校验和保存
1. 在左侧选择刚创建的 Flow,点击右侧 **Focus Flow On Canvas**
2. 使用预览区的播放按钮检查顺序和循环结果。
3. 点击顶部 **Validate**,底部 **Validation** 中不能有 Error。
4. 点击顶部 **Save** 保存。
### Flow 的异步完成配置
选择 Flow 后,可以在右侧 **Animation Flow** 区域设置 **Async Completion**。该配置只在 Flow 的终点为 `Loop` 时生效:
- `Complete On Terminal Loop Start`:默认值。所有前置节点播放完成并显示终点 Loop 第一帧后,异步调用继续执行;终点 Loop 在后台持续循环。
- `Wait For Terminal Loop First Cycle`:等待前置节点和终点 Loop 第一轮全部播放完成后,异步调用继续执行。
终点不是 `Loop` 时,两种配置行为相同,都会等待整条 Flow 自然结束。
## 4. Yarn 调用
### 初始化角色
帧动画角色首次出现时,先初始化:
```yarn
<<init_actor 火山 clinic FrameAnimation>>
```
参数依次为:
```text
角色名 槽位名 角色类型
```
`火山` 会加载 Addressable 地址为 `FrameAnimation/火山` 的 Graph。`init_actor` 会等待角色 Prefab 和 Graph 加载完成,因此下一行可以直接切动画。
同一段角色出场流程中只需初始化一次,不要在每次换动画前重复初始化。
> 初始化完成后不会自动播放 Graph 中的 Default Playable,需要再调用一次 Clip 或 Flow。
### 调用一个 Clip
**Clip ID** 直接写在命令的第一个参数中:
```yarn
<<change_actor_state 捂头表情idle 火山>>
```
这条命令会直接播放 `火山Graph` 中的 `捂头表情idle` Clip。Clip 播完后的表现由该 Clip 的 **End Behavior** 决定:
- `Loop`:持续循环,直到被下一次状态切换替换;
- `HoldLastFrame`:播放一次并停在最后一帧;
- `Clear`:播放一次后清空图片;
- `HideTarget`:播放一次后隐藏角色渲染目标。
#### Clip:播放后立刻继续 Yarn
```yarn
<<change_actor_state "伸手 石化表情" 火山>>
hs: 我太伤心了。
```
`change_actor_state` 只负责开始播放,Yarn 不会等 Clip 播完,会立即执行下一行。适用于:
- 切换 idle 或持续循环表情;
- Clip 需要和对白同时播放;
- 后续时机由策划自己用 `wait` 控制。
#### Clip:播放完成后再继续 Yarn
```yarn
<<change_actor_state_async 扣头切屏特效 火山>>
<<change_actor_state 扣头表情idle 火山>>
```
`change_actor_state_async` 会等待 Clip
- 非循环 Clip:等待整段播放结束;
- `Loop` Clip:等待第一轮播放结束,然后继续执行 Yarn;动画本身仍会循环。
因此,一次性动作之后要准确切换 idle 时,推荐使用上面的“异步动作 Clip → 循环 idle Clip”写法,不需要猜测 `wait` 秒数。
### 调用一个 Flow
Flow 的调用格式与 Clip 完全相同,只需把第一个参数换成 **Flow ID**。例如已配置 `摘帽到伸手_Flow`
#### Flow:播放后立刻继续 Yarn
```yarn
<<change_actor_state 摘帽到伸手_Flow 火山>>
hs: 戴上“实实”牌帽子,给你的头顶添件宝!
```
Yarn 会立即继续对白,Flow 则在后台按照连线依次播放。适合整段表演与对白同时发生的情况。
#### Flow:按 Flow 配置等待后再继续 Yarn
```yarn
<<change_actor_state_async 摘帽到伸手_Flow 火山>>
// 默认在前置动作结束、终点 Loop 首帧显示后执行这里
```
`change_actor_state_async` 会从入口开始等待整条 Flow
- 终点为非循环 Clip:等待所有节点自然播放结束;
- `前置动作 → 终点 Loop` 的 Flow:根据 **Async Completion**,在终点 Loop 开始时完成,或等待其第一轮结束。
如果终点是 `Loop`,命令返回后终点 Clip 仍会继续循环,直到被下一次状态切换替换。
### 快速选择命令
| 要播放的内容 | 希望 Yarn 是否等待 | 写法 |
| --- | --- | --- |
| 单个 Clip | 不等待 | `<<change_actor_state ClipID 角色名>>` |
| 单个 Clip | 等完整动画;Loop 等第一轮 | `<<change_actor_state_async ClipID 角色名>>` |
| 一整条 Flow | 不等待 | `<<change_actor_state FlowID 角色名>>` |
| 一整条 Flow | 按 Flow 的 Async Completion 等待 | `<<change_actor_state_async FlowID 角色名>>` |
命令本身不需要标明目标是 Clip 还是 Flow。系统会用 ID 在当前角色的 Graph 中查找;因此 Clip ID 和 Flow ID 不能重名。
### 名称中有空格时
Clip ID、Flow ID、角色名或槽位名中包含空格时,必须加英文双引号:
```yarn
<<change_actor_state "伸手 石化表情" 火山>>
<<change_actor_state_async "摘帽 到 伸手_Flow" 火山>>
```
不含空格时可以不加;为减少出错,也可以统一加英文双引号。
## 5. 火山的完整示例
下面同时演示 Clip 和 Flow 调用,并假设已按前文创建 `摘帽到伸手_Flow`
```yarn
<<init_actor 火山 clinic FrameAnimation>>
// 调用 Loop Clip:立即继续 Yarnidle 在对白期间持续循环
<<change_actor_state 捂头表情idle 火山>>
<<fade_in_actor 火山>>
hs: 医——生——救——我——!
// 调用 Flow:默认等“摘帽”播完,并显示终点“伸手表情idle”首帧
<<change_actor_state_async 摘帽到伸手_Flow 火山>>
// Flow 返回后,终点的伸手 idle 仍在循环
hs: 戴上“实实”牌帽子,给你的头顶添件宝!
// 调用一次性 Clip,并等待它完整播完
<<change_actor_state_async "扣头切屏特效" 火山>>
// 再调用另一个 Loop Clip,替换当前状态并继续对白
<<change_actor_state "扣头表情idle" 火山>>
hs: 我……没有活干了。
```
## 6. 提交前检查
- Flow 的入口是否是第一个动作,而不是终点 idle?
- 节点是否按预期连线,且没有分支或环?
- 只有最后一个 Node 设置了结束行为吗?
- 需要持续显示的 idle 是否设置为 `Loop`
- Flow ID 是否与已有 Clip / Flow 重名?
- Flow 的 **Async Completion** 是否符合剧情节奏?
- Yarn 中的 ID 与 Graph 完全一致,包括空格和大小写吗?
- 需要等动画时是否用了 `change_actor_state_async`
- 顶部 **Validate** 是否无 Error
- 是否点击 **Save**
## 7. 常见问题
**调用后没有动画**
先检查角色是否用 `FrameAnimation` 类型初始化,再检查 Graph 的 Addressable 地址是否为 `FrameAnimation/角色名`。火山应为 `FrameAnimation/火山`
**提示 playable 找不到**
Yarn 中传入的是 Clip ID 或 Flow ID,不是资源文件名、Graph 名或 Node 的 Display Name。检查字符、空格和大小写是否完全一致。
**动画刚开始就被打断**
后面很可能紧跟了另一条 `change_actor_state`。普通命令不会等待;需要等当前动画完成时改用 `change_actor_state_async`
**循环动画导致剧情无法继续**
直接异步播放 Loop Clip 时只等待第一轮。异步播放 Flow 时,默认进入终点 Loop 即继续,也可以通过 **Async Completion** 配置为等待终点 Loop 第一轮。如果仍未继续,先检查是否还停在前置节点、有效速度是否为 0,并运行 **Validate** 检查 Flow 路径和结尾配置。
**想让 Flow 中途停住**
当前 Flow 是顺序播放,不支持分支或中途等待 Yarn。应拆成两个 Clip / Flow,在 Yarn 中分两次调用。