272 lines
8.2 KiB
Markdown
272 lines
8.2 KiB
Markdown
# TimelineKit 使用文档
|
||
|
||
TimelineKit 是 AIBIS Dream 项目中用于统一管理 Unity Timeline 播放的中心化系统。它基于 `PlayableDirector`,提供播放控制、Addressables 动态加载、Yarn 对话集成等功能。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [系统架构](#系统架构)
|
||
2. [快速开始](#快速开始)
|
||
3. [配置 DirectorHandler](#配置-directorhandler)
|
||
4. [TimelineCenter API](#timelinecenter-api)
|
||
5. [Timeline 名称格式](#timeline-名称格式)
|
||
6. [Yarn 对话集成](#yarn-对话集成)
|
||
7. [代码调用示例](#代码调用示例)
|
||
8. [资源组织](#资源组织)
|
||
9. [Addressables 配置](#addressables-配置)
|
||
10. [常见问题](#常见问题)
|
||
|
||
---
|
||
|
||
## 系统架构
|
||
|
||
```
|
||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||
│ TimelineCenter │────▶│ DirectorHandler │────▶│ PlayableDirector│
|
||
│ (单例中心) │ │ (场景中的组件) │ │ (Unity 原生) │
|
||
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||
│
|
||
├── 注册/注销 Director
|
||
├── 播放控制 (播放/暂停/停止/重置)
|
||
├── 状态查询 (时间/时长)
|
||
└── 批量操作
|
||
```
|
||
|
||
- **TimelineCenter**:单例管理器,负责注册所有 Director 并统一调度
|
||
- **DirectorHandler**:挂载在带 `PlayableDirector` 的 GameObject 上,自动向 TimelineCenter 注册
|
||
- **TimelineYarnCommand**:提供 Yarn Spinner 命令,可在对话脚本中直接播放 Timeline
|
||
|
||
---
|
||
|
||
## 快速开始
|
||
|
||
### 1. 场景设置
|
||
|
||
1. 在场景中创建或选中一个 GameObject
|
||
2. 添加 **PlayableDirector** 组件,并分配默认的 Playable Asset
|
||
3. **将 PlayableDirector 的 Play On Awake 设为 false**,否则 Timeline 会在场景加载时立刻播放
|
||
4. 添加 **DirectorHandler** 组件
|
||
5. 在 DirectorHandler 中填写 **Timeline Name**(唯一标识,用于 PlayTimeline 时指定)
|
||
|
||
### 2. 播放 Timeline
|
||
|
||
```csharp
|
||
// 播放该 Director 自带的 Playable Asset
|
||
TimelineCenter.Instance.PlayTimeline("维修面板");
|
||
|
||
// 从 Addressables 加载并播放
|
||
TimelineCenter.Instance.PlayTimeline("卡扣/卡扣解锁");
|
||
```
|
||
|
||
---
|
||
|
||
## 配置 DirectorHandler
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| **timelineName** | Director 的全局唯一名称,供 PlayTimeline 等 API 使用 |
|
||
| **director** | 关联的 PlayableDirector(通常与组件同一 GameObject,可自动查找) |
|
||
|
||
DirectorHandler 会在 `Start` 时自动向 TimelineCenter 注册,在 `OnDestroy` 时注销。
|
||
|
||
---
|
||
|
||
## TimelineCenter API
|
||
|
||
### 播放控制
|
||
|
||
| 方法 | 说明 |
|
||
|------|------|
|
||
| `PlayTimeline(string timelineName)` | 播放 Timeline(同步,不等待结束) |
|
||
| `PlayTimelineAsync(string name)` | 协程版本,播放完成后 yield 返回 |
|
||
| `StopTimeline(string timelineName)` | 停止播放 |
|
||
| `PauseTimeline(string timelineName)` | 暂停 |
|
||
| `ResumeTimeline(string timelineName)` | 恢复播放 |
|
||
| `ResetTimeline(string timelineName)` | 重置到开头 |
|
||
| `HideTimeline(string timelineName)` | 隐藏该 Director 的 GameObject(SetActive(false)) |
|
||
|
||
### 状态查询
|
||
|
||
| 方法 | 说明 |
|
||
|------|------|
|
||
| `GetPlaybackTime(string name)` | 获取当前播放时间(秒) |
|
||
| `GetDuration(string name)` | 获取 Timeline 总时长(秒) |
|
||
|
||
### 扩展操作
|
||
|
||
| 方法 | 说明 |
|
||
|------|------|
|
||
| `SetPlaybackSpeed(string name, float speed)` | 设置播放速度 |
|
||
| `SeekToTime(string name, double time)` | 跳转到指定时间点 |
|
||
|
||
### 批量操作
|
||
|
||
| 方法 | 说明 |
|
||
|------|------|
|
||
| `PauseAll()` | 暂停所有已注册的 Timeline |
|
||
| `StopAll()` | 停止所有 |
|
||
| `ResetAll()` | 重置所有 |
|
||
|
||
---
|
||
|
||
## Timeline 名称格式
|
||
|
||
`timelineName` 支持两种格式:
|
||
|
||
| 格式 | 示例 | 说明 |
|
||
|------|------|------|
|
||
| **仅 Director 名** | `"维修面板"` | 播放该 Director 自带的 `playableAsset` |
|
||
| **Director 名/AddressableKey** | `"卡扣/卡扣解锁"` | 从 Addressables 加载 `Timeline/{AddressableKey}` 并播放 |
|
||
|
||
解析规则:若包含 `/`,则前半部分为 directorName,后半部分为 addressableKey;否则整个字符串为 directorName。
|
||
|
||
---
|
||
|
||
## Yarn 对话集成
|
||
|
||
TimelineYarnCommand 提供以下 Yarn 命令,可在 `.yarn` 文件中直接使用:
|
||
|
||
### play_timeline
|
||
|
||
播放 Timeline,**会等待播放完成**后再继续对话。
|
||
|
||
```yarn
|
||
// 播放自带 Timeline
|
||
<<play_timeline 维修面板>>
|
||
|
||
// 播放 Addressable Timeline
|
||
<<play_timeline 卡扣/卡扣解锁>>
|
||
```
|
||
|
||
### reset_timeline
|
||
|
||
重置 Timeline 到开头。
|
||
|
||
```yarn
|
||
<<reset_timeline 维修面板>>
|
||
```
|
||
|
||
### hide_timeline
|
||
|
||
隐藏 Timeline 所在的 GameObject。
|
||
|
||
```yarn
|
||
<<hide_timeline 维修面板>>
|
||
```
|
||
|
||
### start_timeline / wait_timeline
|
||
|
||
`start_timeline` 起播但**不等待**;`wait_timeline` 等待**已经在播**的 Timeline 结束(不重头播)。叙事切场景时常用:黑屏下 `start` → 短 wait 稳住首帧 → `fade_out` → `wait_timeline`。完整约定见项目 Skill [`.cursor/skills/narrative-timeline-flow/SKILL.md`](../../../../.cursor/skills/narrative-timeline-flow/SKILL.md)。
|
||
|
||
```yarn
|
||
<<start_timeline 英里初登场>>
|
||
<<wait 0.2>>
|
||
<<fade_out>>
|
||
<<wait_timeline 英里初登场>>
|
||
<<hide_timeline 英里初登场>>
|
||
```
|
||
|
||
---
|
||
|
||
## 代码调用示例
|
||
|
||
### 直接播放(不等待)
|
||
|
||
```csharp
|
||
TimelineCenter.Instance.PlayTimeline("维修面板");
|
||
```
|
||
|
||
### 协程中等待播放完成
|
||
|
||
```csharp
|
||
IEnumerator PlayAndDoSomething()
|
||
{
|
||
yield return TimelineCenter.Instance.PlayTimelineAsync("卡扣/卡扣解锁");
|
||
// 播放完成后执行
|
||
Debug.Log("Timeline 播放完毕");
|
||
}
|
||
```
|
||
|
||
### 配合 ActionKit 使用
|
||
|
||
```csharp
|
||
ActionKit.Sequence()
|
||
.Coroutine(() => TimelineCenter.Instance.PlayTimelineAsync("卡扣/卡扣解锁"))
|
||
.Callback(() => BlockPuzzleYarnCommand.UnlockSystem())
|
||
.Start(this);
|
||
```
|
||
|
||
### 带完成回调的 DirectorHandler 播放
|
||
|
||
若直接持有 DirectorHandler 引用,可传入 `onComplete`:
|
||
|
||
```csharp
|
||
// 仅适用于 Director 自带的 Asset,非 Addressable
|
||
directorHandler.Play(() => Debug.Log("播放完成"));
|
||
```
|
||
|
||
---
|
||
|
||
## 资源组织
|
||
|
||
- **Timeline 文件建议放在 `Assets/Animation` 文件夹下**,便于统一管理。
|
||
|
||
## Addressables 配置
|
||
|
||
对于需要动态加载的 Timeline 文件,**Addressable 的 Address 格式为 `Timeline/xxx`**。使用 `DirectorName/AddressableKey` 格式调用时,DirectorHandler 会加载:
|
||
|
||
```
|
||
Timeline/{AddressableKey}
|
||
```
|
||
|
||
因此需在 Addressables 中:
|
||
|
||
1. 创建或使用已有 Group(如「石头维修相关资源」)
|
||
2. 将 Timeline 资源加入 Group
|
||
3. **设置 Address 为 `Timeline/xxx` 格式**(如 `Timeline/卡扣解锁`)
|
||
|
||
示例(`石头维修相关资源.asset` 中已有):
|
||
|
||
- `Timeline/卡扣解锁`
|
||
- `Timeline/卡扣锁定`
|
||
|
||
代码中调用时,`DirectorName/AddressableKey` 的 addressableKey 部分填 `卡扣解锁` 即可,DirectorHandler 会自动补全 `Timeline/` 前缀。
|
||
|
||
---
|
||
|
||
## 常见问题
|
||
|
||
### Q: Timeline 一进场景就自动播放?
|
||
|
||
将 PlayableDirector 组件的 **Play On Awake** 设为 **false**。否则 Unity 会在 Awake 时自动播放,与 TimelineCenter 的播放控制冲突。
|
||
|
||
### Q: Timeline 找不到?
|
||
|
||
确保场景中对应 GameObject 上有 DirectorHandler,且 `timelineName` 与调用时一致。DirectorHandler 在 `Start` 时注册,若场景未完全加载可能尚未注册。
|
||
|
||
### Q: Addressable 加载失败?
|
||
|
||
检查:
|
||
|
||
1. Addressable 的 Address 是否包含 `Timeline/` 前缀,或与 DirectorHandler 拼出的 key 一致
|
||
2. 资源已正确加入 Addressables Group
|
||
3. 控制台是否有 `Failed to load PlayableAsset` 等错误
|
||
|
||
### Q: 播放完成回调不触发?
|
||
|
||
- `PlayTimeline` / `PlayTimelineAsync` 不直接支持回调,需用协程或 ActionKit 在播放完成后再执行逻辑
|
||
- 直接调用 `directorHandler.Play(onComplete)` 时可使用回调,但仅适用于自带 Asset
|
||
|
||
### Q: 多个 Director 重名?
|
||
|
||
`timelineName` 必须全局唯一,否则后注册的会覆盖先前的。
|
||
|
||
---
|
||
|
||
## 相关文件
|
||
|
||
- `TimelineCenter.cs` - 中心管理器
|
||
- `DirectorHandler.cs` - Director 组件
|
||
- `TimelineYarnCommand.cs` - Yarn 命令注册
|