From c4dbdc098bcba5defa4d3492ce1f7c64ab6b0975 Mon Sep 17 00:00:00 2001 From: bottlefish <781230111@qq.com> Date: Mon, 13 Jul 2026 22:52:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(timeline):=20=E6=B7=BB=E5=8A=A0=20wait=5Ft?= =?UTF-8?q?imeline=20=E4=BB=A5=E6=94=AF=E6=8C=81=E9=BB=91=E5=B1=8F?= =?UTF-8?q?=E8=B5=B7=E6=92=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .../skills/narrative-timeline-flow/SKILL.md | 94 +++++++++++++++++++ .../TimelineKit/DirectorHandler.cs | 16 ++++ .../TimelineKit/README_TimelineKit.md | 12 +++ .../TimelineKit/TimelineCenter.cs | 15 +++ .../TimelineKit/TimelineYarnCommand.cs | 9 ++ 5 files changed, 146 insertions(+) create mode 100644 .cursor/skills/narrative-timeline-flow/SKILL.md diff --git a/.cursor/skills/narrative-timeline-flow/SKILL.md b/.cursor/skills/narrative-timeline-flow/SKILL.md new file mode 100644 index 000000000..35bb6c7ab --- /dev/null +++ b/.cursor/skills/narrative-timeline-flow/SKILL.md @@ -0,0 +1,94 @@ +--- +name: narrative-timeline-flow +description: >- + 叙事 Timeline:aseprite/静图改 Timeline、Addressable 登记、黑屏防穿帮、替换 show_full_screen。 + Use when 加叙事 timeline、aseprite 转 timeline、英里初登场、CG 演出、替换 show_full_screen、 + hide_full_screen、黑屏穿帮、fade_out 时机、wait_timeline、Day1 begin / Day2 night 天桥 CG、 + 或用户提到 TimelineKit 叙事接入。 +--- + +# 叙事 Timeline 接入(Aseprite / 静图 → Yarn) + +API 细节见 [`Assets/Scripts/SceneManagement/TimelineKit/README_TimelineKit.md`](../../Assets/Scripts/SceneManagement/TimelineKit/README_TimelineKit.md)。本 skill 只记**本仓库约定与踩坑**。 + +## 1. 何时用哪种 Yarn 命令 + +| 场景 | 命令 | +|------|------| +| 播完再往下走 | `<>`(阻塞) | +| 黑屏下先起播 / 与对白并行 / Loop CG | `<>` | +| 已 `start_timeline`,要等播完 | `<>`(不重头播) | +| 关掉整棵 Director GO | `<>` | + +名称与场景里 `DirectorHandler.timelineName` **完全一致**(叙事多用中文单名,内嵌 Playable)。 + +## 2. 资产与 Addressable + +1. Aseprite → `Assets/GameContent/Scene_{场景}/animationRaw/`(或现有 `AnimationRaw/`) +2. Timeline → `GameContent/Scene_{场景}/Timeline/{名}.playable` +3. Addressable Group = 对应场景线(如 `Scene_ClinicOut`、`Scene_Bridge`) +4. Address = `Timeline/{名}`(必须带 `Timeline/` 前缀) +5. 叙事 Timeline **一般不**加 `TimelineAssetRef`(维修玩法才用) + +参考范本:`医生出地铁`、`day2夕阳医生看佩佩侧脸`、`英里初登场`。 + +## 3. 场景 GO 约定 + +- 根节点挂 `PlayableDirector`(`Play On Awake = false`)+ `DirectorHandler` +- **根节点默认 Active**:`Start` 里要注册到 `TimelineCenter`;关掉根节点会注册失败 +- **带 Sprite 的子物体(如 `Visual`)默认 Inactive**;用 ActivationTrack 或播放时再显 +- `hide_timeline` = 根 `SetActive(false)`;之后再播时 `PlayTimeline` 会重新打开 +- 全屏 CG 常需调位置(例:天桥侧脸根节点 `Y = -5`);PPU / scale 按相机再调 + +## 4. 黑屏防穿帮(必做) + +**错误**:`fade_out` 揭开场景 → 再 `play_timeline`(先露底再播 CG)。 + +**正确**(切场景后的 intro CG): + +```yarn +<> +<> +<> +<> +<> +<> +<> +<> +<> +<> +``` + +要点:黑屏内起播并稳住首帧 → 再结束黑屏 → 再等播完。CG 盖住初登场时,角色可延后到 `hide_timeline` 后再 `show_actor`。 + +## 5. 替换 `show_full_screen` 静图 + +常驻 / Loop 演出(对白期间一直播): + +```yarn +<> +<> +# ... 对白 ... +<> +<> +<> +``` + +不要再用 `show_full_screen` / `hide_full_screen` 播同一段。 + +## 6. 其它坑 + +- `<>` **只需场景名**。旧脚本第二参数(如 `1`)已兼容忽略,**新代码不要写**。 +- 改 `.yarn`:**不要动**已有 `#line:`;新行不加 `#line:`。 +- 只改运行时实际绑定的 Yarn(如 Day1_begin → FP,不是 Fiction 草稿)。 +- Aseprite 多 tag:选主演出 clip;Loop CG 用循环 clip + Director `Loop` 或长轨;一次性 intro 用有限时长 + `wait_timeline`。 + +## 7. 落地检查清单 + +- [ ] Timeline 在正确 `GameContent/Scene_* /Timeline/` +- [ ] Addressable:`Timeline/{名}` + 正确 Group +- [ ] `timelineName` 与 Yarn 一致;`Play On Awake = false` +- [ ] Visual 默认关;进 Play 不会提前露 CG +- [ ] 切场景 intro:黑屏内 `start` → 短 wait → `fade_out` → `wait_timeline` +- [ ] 静图替换:`start` / `hide` 成对 +- [ ] 跑一次确认无先露场景、无 `load_scene` 参数错误 diff --git a/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs b/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs index b82efe24c..c0cb6ad29 100644 --- a/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs +++ b/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs @@ -266,6 +266,22 @@ namespace AibisDream } } + /// + /// 等待当前播放结束(不 Stop、不重头播放)。用于 start_timeline 后再等播完。 + /// + public IEnumerator WaitUntilStoppedAsync() + { + if (director == null) + { + yield break; + } + + while (director.state == PlayState.Playing) + { + yield return null; + } + } + /// /// 从 Addressables 加载 PlayableAsset 并播放(加载时自动加 "Timeline/" 前缀) /// diff --git a/Assets/Scripts/SceneManagement/TimelineKit/README_TimelineKit.md b/Assets/Scripts/SceneManagement/TimelineKit/README_TimelineKit.md index b39d32711..2d089da6a 100644 --- a/Assets/Scripts/SceneManagement/TimelineKit/README_TimelineKit.md +++ b/Assets/Scripts/SceneManagement/TimelineKit/README_TimelineKit.md @@ -155,6 +155,18 @@ TimelineYarnCommand 提供以下 Yarn 命令,可在 `.yarn` 文件中直接使 <> ``` +### 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 +<> +<> +<> +<> +<> +``` + --- ## 代码调用示例 diff --git a/Assets/Scripts/SceneManagement/TimelineKit/TimelineCenter.cs b/Assets/Scripts/SceneManagement/TimelineKit/TimelineCenter.cs index 0417aede0..ad1632106 100644 --- a/Assets/Scripts/SceneManagement/TimelineKit/TimelineCenter.cs +++ b/Assets/Scripts/SceneManagement/TimelineKit/TimelineCenter.cs @@ -147,6 +147,21 @@ namespace AibisDream directorHandler.gameObject.SetActive(false); } + /// + /// 等待已在播放的 Timeline 结束(不重新从头播放)。 + /// + public IEnumerator WaitTimelineAsync(string timelineName) + { + var (directorName, _) = ParseTimelineName(timelineName); + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) + { + Debug.LogError($"Timeline {timelineName} not found (directorName: {directorName})"); + yield break; + } + + yield return directorHandler.WaitUntilStoppedAsync(); + } + #region 状态查询 public double GetPlaybackTime(string name) diff --git a/Assets/Scripts/SceneManagement/TimelineKit/TimelineYarnCommand.cs b/Assets/Scripts/SceneManagement/TimelineKit/TimelineYarnCommand.cs index f4ef50cfa..e205d8335 100644 --- a/Assets/Scripts/SceneManagement/TimelineKit/TimelineYarnCommand.cs +++ b/Assets/Scripts/SceneManagement/TimelineKit/TimelineYarnCommand.cs @@ -20,6 +20,15 @@ namespace AibisDream TimelineCenter.Instance.PlayTimeline(timelineName); } + /// + /// 等待已在播放的 Timeline 结束(配合 start_timeline:可先在黑屏下起播,fade_out 后再等播完)。 + /// + [YarnCommand("wait_timeline")] + public static IEnumerator WaitTimeline(string timelineName) + { + yield return TimelineCenter.Instance.WaitTimelineAsync(timelineName); + } + [YarnCommand("reset_timeline")] public static void ResetTimeline(string timelineName) {