Codex实装,RegularGridFloor

This commit is contained in:
SoulliesOfficial
2026-07-08 04:29:20 -04:00
parent 7b7d069b84
commit d474a8929c
176 changed files with 31884 additions and 890 deletions

View File

@@ -1,439 +0,0 @@
## Plan: 剧情系统重构(故事树 + Yarn Spinner 对话VN 风格)
## Context
**目标项目:** `ichni Official`Unity 6000.3URPWwise。剧情系统为 Official 独有(`IchniCreatorStudio``Story` 模块),因此本次重构**不涉及双项目同步**,也不涉及 BM 类。
**要做什么、为什么:** 现有剧情系统在引入 Yarn Spinner 之前编写,对话处理(`DialogManager` + 自定义文本解析 + DynamicExpresso 解释器 + `DialogUI`)已过时。本计划**从零重构**整个剧情系统:数据、故事树 UI、存档、对话全部重来改用 **Yarn Spinner 3.2.1** 驱动对话,并采用**视觉小说VN风格**呈现(多角色立绘、打字机、表现类 Yarn 命令),对话文本经 **Unity Localization** 统一管理多语言。
**当前系统关键事实(将被替换):**
- `StoryData``SerializedScriptableObject`)按章节定义 `DialogBlockData`/`SongBlockData`/`TutorialBlockData`/`initialBlocks``blockName`/`blockID`/`blockSize`/`blockPosition`/`nextBlocks`)。
- `Storyline``MonoBehaviour`)从 `StoryData` 或 ES3 存档构建节点图,管理 `storyBlocks`/`connectors`,按章节存取;`StoryBlockUIBase``DialogBlockUI`/`SongBlockUI`/`TutorialBlockUI`,点击判断 `StoryBlockState { Locked, Current, Completed }`
- `DialogManager`:自定义解析器(`[段落]{函数}``speaker:句子``$Choice(...)``$Condition{...}`、内联 `{@表达式}`),从 `Resources/Story/[chapter]/Dialogs/` 加载 `TextAsset`
- `StoryInterpreters`DynamicExpresso`SetVariable`/`GetVariable`/`GenerateDialogBlock`/`GenerateSongBlock`/`SetUnlockKey`——不仅分支对话,还动态生成故事树节点、解锁歌曲。
- `DialogUI``DialogUIPage`/`DialogContentFrame`/`DialogTextUI`/`ChoiceGroupUI`/`ChoiceButtonUI`历史记录式scrollback呈现点击推进。
- 存档:`GameSaveManager.StorySaveModule`block 存档、`storyVariables``selectedChoices`ES3 持久化到 `persistentDataPath/StorySaves/[Chapter].json`
- 剧情系统实例位于 `MenuScene.unity``StoryManager``DialogManager` 均在其中)。
**Yarn Spinner 3.2.1 关键 API已核实包源码**
- `DialogueRunner``StartDialogue(nodeName)``UnityEvent onDialogueStart` / `onDialogueComplete``AddCommandHandler(name, Delegate)``dialogueViews``DialoguePresenterBase` 列表)。异步架构(`YarnTask`)。
- `DialoguePresenterBase``Runtime/Views/DialoguePresenterBase.cs`override `YarnTask RunLineAsync(LocalizedLine, LineCancellationToken)``YarnTask<DialogueOption?> RunOptionsAsync(DialogueOption[], LineCancellationToken)``YarnTask OnDialogueStartedAsync()``YarnTask OnDialogueCompleteAsync()`;可选 `OnNodeEnter/OnNodeExit`
- `VariableStorageBehaviour``Runtime/Storage/VariableStorageBehaviour.cs`):自定义变量存储基类。
- `UnityLocalisedLineProvider``Runtime/LineProviders/UnityLocalisedLineProvider.cs``.Installed.cs` 分部类需安装 `com.unity.localization` 才编译):桥接 Unity Localization String Table。
- 表现命令用 `[YarnCommand("name")]` 静态/实例方法或 `AddCommandHandler` 注册。
**设计决策(已与你确认):**
1. **全部从零重构**数据、UI、存档
2. **VN 风格呈现** + 多角色立绘 + 表现类 Yarn 命令(如立绘跳动/移动)。
3. **混合式解锁**:故事树默认在 `StoryData` 静态定义;同时允许 Yarn 命令在特殊情况下插入/解锁 block。
4. **完整表现命令集** + 支持多立绘的 VN Presenter立绘渲染**做抽象**(当前 Sprite未来可换 Live2D/Spine
5. **对话本地化桥接 Unity Localization**(弃用 I2由 Unity Localization 统一管理对话与 UI 文本。
6. **仅优先文本块TextBlock**Song/Tutorial block 作为占位符。
**新目录结构(`/ichni Official/Assets/Scripts/Story/`**
```
Story/
├── Data/ # 数据与 ScriptableObject
│ ├── StoryData.cs # 章节故事树定义(重建)
│ ├── StoryBlockDefinition.cs # block 定义基类 + Text/Song/Tutorial + 解锁条件
│ ├── CharacterData.cs # 单个角色定义id/显示名Key/立绘/情绪)
│ └── CharacterRegistry.cs # 角色注册表id -> CharacterData
├── Tree/ # 故事树运行时 + UI
│ ├── StoryManager.cs # 协调器(重建)
│ ├── StoryTreeController.cs # 构建树/状态/解锁重算/存取(原 Storyline
│ ├── StoryBlockView.cs # block 视图基类(原 StoryBlockUIBase
│ ├── TextBlockView.cs # 文本块(原 DialogBlockUI
│ ├── SongBlockView.cs # 占位
│ ├── TutorialBlockView.cs # 占位
│ └── BlockConnectorView.cs # 连接线(原 BlockConnectorUI
├── Dialogue/ # Yarn 集成
│ ├── StoryDialogueController.cs # block → DialogueRunner 桥接
│ ├── VNDialoguePresenter.cs # DialoguePresenterBase 实现VN
│ ├── VNPortraitStage.cs # 立绘舞台(多槽位)
│ ├── IPortraitRenderer.cs # 立绘渲染抽象接口
│ ├── SpritePortraitRenderer.cs # Sprite 实现
│ ├── StoryVariableStorage.cs # VariableStorageBehaviour 桥接存档
│ ├── StoryPresentationCommands.cs # [YarnCommand] 表现命令
│ ├── StoryTreeCommands.cs # [YarnCommand] 解锁/生成 block/解锁歌曲
│ └── StoryFunctions.cs # [YarnFunction] 条件辅助
├── Save/
│ └── StorySave.cs # 存档 DTO重建
└── UI/
└── StoryUIPage.cs # 故事树页面(适配保留)
```
参考页面:@ichni-Project-Architecture-Report
------
## Modifications
### A. Scripts
#### A1. `Story/Data/StoryBlockDefinition.cs`(新建)
- **描述:** 章节故事树中单个 block 的纯数据定义 + 解锁条件。路径 `/ichni Official/Assets/Scripts/Story/Data/StoryBlockDefinition.cs`
- **遵循模式:** 参考旧 `StoryData.cs``[Serializable]`/`[InlineProperty]`/`[FoldoutGroup("$blockName")]` Odin 风格。
- **内容:**
- `enum StoryBlockType { Text, Song, Tutorial }`
- `[Serializable] class StoryBlockDefinition``string blockId`(唯一,替代旧 `blockName`/`blockID` 二元)、`StoryBlockType type``Vector2 position``Vector2 size``StoryBlockState initialState``List<string> nextBlockIds``UnlockCondition unlockCondition`
- 类型专属数据(用 Odin `[ShowIf]``type` 显示):`string yarnNodeName` + `string titleKey`Text`string songName`Song 占位);`string tutorialName`Tutorial 占位)。
- `[Serializable] class UnlockCondition``List<string> requiredCompletedBlockIds``List<VariableCondition> variableConditions`;方法 `bool IsSatisfied(Func<string,int> getVar, Func<string,bool> isCompleted)`
- `[Serializable] class VariableCondition``string variableName``enum Comparison { Equal, NotEqual, Greater, GreaterOrEqual, Less, LessOrEqual }``int value``bool Evaluate(int actual)`
- **公共 API** `bool UnlockCondition.IsSatisfied(...)``bool VariableCondition.Evaluate(int)`
#### A2. `Story/Data/StoryData.cs`(重建,覆盖现有)
- **描述:** 章节故事树 ScriptableObject。路径 `/ichni Official/Assets/Scripts/Story/Data/StoryData.cs`
- **内容:** `[CreateAssetMenu(menuName="Ichni/Story/StoryData")] class StoryData : SerializedScriptableObject``string chapterIndex``List<StoryBlockDefinition> blocks``YarnProject yarnProject`(该章节编译产物引用)、`AssetReference/string` 本地化 String Table 集合名(供 Presenter/Provider 使用)。
- **公共 API** `StoryBlockDefinition GetBlock(string blockId)``IEnumerable<StoryBlockDefinition> GetInitialBlocks()``IEnumerable<StoryBlockDefinition> GetNextBlocks(string blockId)`
- **依赖:** A1、Yarn `YarnProject`
#### A3. `Story/Data/CharacterData.cs` + `CharacterRegistry.cs`(新建)
- **描述:** 角色立绘/情绪注册表VN Presenter 需要)。路径 `/ichni Official/Assets/Scripts/Story/Data/`
- `**CharacterData``[Serializable]`** `string characterId`(与 Yarn 行的 speaker 名对应)、`string displayNameKey`Unity Localization 键)、`PortraitStyle portraitStyle``enum { Sprite, Live2D, Spine }` 预留)、`SerializedDictionary<string, Sprite> emotionSprites`Sprite 方案)、`GameObject dynamicPortraitPrefab`Live2D/Spine 预留,实现 `IPortraitRenderer`)。
- `**CharacterRegistry``[CreateAssetMenu(menuName="Ichni/Story/CharacterRegistry")] SerializedScriptableObject`** `Dictionary<string, CharacterData> characters``bool TryGet(string id, out CharacterData data)`
- **说明:** 立绘表现不直接耦合 Sprite交由 `IPortraitRenderer`(见 A9读取 `CharacterData` 决定渲染方式,未来接入 Live2D/Spine 只需新增 renderer 实现。
#### A4. `Story/Save/StorySave.cs`(重建,覆盖现有)
- **描述:** 存档 DTO。路径 `/ichni Official/Assets/Scripts/Story/Save/StorySave.cs`
- **内容:** `[Serializable] class StoryBlockSave { string blockId; Vector2 position; StoryBlockState state; List<string> nextBlockIds; }`(合并旧的按类型拆分的多个 Save 类为单一带 `blockId` 的 DTO类型信息从 `StoryData` 反查,存档只存运行态);`[Serializable] class ChapterStorySave { string chapterIndex; List<StoryBlockSave> blocks; }`
- **依赖:** A1`StoryBlockState`、block 类型引用)。
#### A5. `Story/Tree/StoryManager.cs`(重建,覆盖现有)
- **描述:** 剧情系统协调器(单例)。路径 `/ichni Official/Assets/Scripts/Story/Tree/StoryManager.cs`
- **遵循模式:** 参考现有 `StoryManager` 单例 + `[Button] ClearAllStorySave()`;建议继承 `SLSUtilities.General.Singleton<StoryManager>`(项目已有单例基类)。
- **内容:** 持有 `StoryTreeController treeController``StoryDialogueController dialogueController``StoryUIPage storyUIPage``CharacterRegistry characterRegistry``Dictionary<string, StoryData> storyDatas`。保留 `enum StoryBlockState { Locked, Current, Completed }`(移到此文件或独立文件)。保留 `ClearAllStorySave()`(改为调用新 `StorySaveModule` 接口)。
- **公共 API** `void OpenChapter(string chapterIndex)`(构建树)、`StoryData GetStoryData(string chapterIndex)`
#### A6. `Story/Tree/StoryTreeController.cs`(重建自 `Storyline.cs`
- **描述:** 从 `StoryData`/存档构建 block 视图与连接线,管理状态、解锁重算与存取。路径 `/ichni Official/Assets/Scripts/Story/Tree/StoryTreeController.cs`
- **遵循模式:** 参考旧 `Storyline.cs``SetUpStoryline`/`Generate*Block`/`GenerateConnector`/`SaveStoryline`/`ResetStory` 结构与 `ScrollRect content` 布局逻辑。
- **公共 API**
- `void BuildChapter(string chapterIndex)`:优先 `LoadChapter`,无存档则 `ResetToInitial`
- `StoryBlockView GenerateBlock(StoryBlockDefinition def)` / `void GenerateConnector(string fromBlockId, string toBlockId)`
- `void OnBlockCompleted(string blockId)`:设该 block `Completed`,遍历所有 `Locked` block 用 `UnlockCondition.IsSatisfied(...)` 重算,满足者置 `Current`(并按需生成尚未显示的后继 block/连接线),刷新连接线,保存。
- `void UnlockBlock(string blockId)` / `void InsertBlock(StoryBlockDefinition def, string fromBlockId)`:供 Yarn 树命令A11调用实现"混合式"动态解锁/插入。
- `void SaveChapter()` / `void LoadChapter(string chapterIndex)` / `void ResetToInitial(string chapterIndex)`
- `StoryBlockView currentBlock { get; }``IReadOnlyList<StoryBlockView> blocks`
- **解锁判定:** `IsSatisfied``getVar` 委托指向 `StoryVariableStorage`A8`isCompleted` 指向已完成 block 集合。
- **依赖:** A1、A2、A4、A8、`GameSaveManager.StorySaveModule`(重建后,见 A13`ChapterSelectionManager`
#### A7. `Story/Tree/StoryBlockView.cs` + `TextBlockView.cs` + `SongBlockView.cs` + `TutorialBlockView.cs` + `BlockConnectorView.cs`(重建自 Blocks/*
- `**StoryBlockView`(抽象,`MonoBehaviour`** `string blockId``StoryBlockState state``RectTransform inPort/outPort``Button button``virtual void ApplyState(StoryBlockState)`(更新视觉:锁定/当前/已完成外观);`abstract StoryBlockSave GetSave()``abstract void OnClicked()`
- `**TextBlockView`** 覆盖 `OnClicked()``state == Locked` → 忽略;否则 `StoryManager.instance.dialogueController.PlayBlock(this)`(传 `yarnNodeName`)。参考旧 `DialogBlockUI``button.onClick` + 状态判断。
- `**SongBlockView` / `TutorialBlockView`** 占位实现——显示标题/状态,`OnClicked()` 暂仅 `Debug.Log`(保留旧 `SongBlockUI`/`TutorialBlockUI` 视觉字段,交互逻辑注释或占位)。
- `**BlockConnectorView`** 沿用旧 `BlockConnectorUI`,用 `UILineRenderer``outPort``inPort` 画曲线;`SetCurve()`
- **依赖:** A6、A9对话触发
#### A8. `Story/Dialogue/StoryVariableStorage.cs`(新建)
- **描述:** 自定义 Yarn 变量存储,桥接 `GameSaveManager.StorySaveModule.storyVariables`ES3 持久化。路径 `/ichni Official/Assets/Scripts/Story/Dialogue/StoryVariableStorage.cs`
- **遵循模式:** 继承 `Yarn.Unity.VariableStorageBehaviour`,实现其抽象/虚方法(`SetValue`/`TryGetValue`/`Clear`/`Contains` 等——**实现步骤时先 `printAsset` 或 view `VariableStorageBehaviour.cs` 核对当前签名**)。
- **要点:** Yarn 变量以 `$` 前缀命名;本存储把布尔/数值/字符串映射到 `storyVariables`(现为 `Dictionary<string,int>`,需扩展为支持多类型,见 A13。变量读写触发 `StorySaveModule` 持久化。
- **公共 API供 A6 解锁判定复用):** `int GetIntVariable(string name)``bool HasVariable(string name)`
- **依赖:** A13`StorySaveModule`)。
#### A9. `Story/Dialogue/IPortraitRenderer.cs` + `SpritePortraitRenderer.cs`(新建)
- **描述:** 立绘渲染抽象 + Sprite 实现。路径 `/ichni Official/Assets/Scripts/Story/Dialogue/`
- `**IPortraitRenderer`** `void Show(CharacterData character, string emotion)``void Hide()``void SetEmotion(string emotion)``void MoveTo(Vector2 anchoredPos, float duration)``void PlayEffect(string effect, float intensity)`(如 "jump"/"shake"/"nod")、`void SetActiveVisual(bool speaking)`(说话者高亮/变暗)。
- `**SpritePortraitRenderer``MonoBehaviour, IPortraitRenderer`** 持有 `UnityEngine.UI.Image``Show``CharacterData.emotionSprites` 取 Sprite`MoveTo`/`PlayEffect` 用 DOTween项目已含 DOTweenPro实现跳动/移动/晃动。
- **说明:** 未来 Live2D/Spine 只需新增实现 `IPortraitRenderer` 的组件,`CharacterData.portraitStyle`/`dynamicPortraitPrefab` 决定实例化哪个 renderer。
#### A10. `Story/Dialogue/VNPortraitStage.cs`(新建)
- **描述:** 管理多立绘槽位(如 Left/Center/Right按 characterId 分配/复用 renderer。路径 `/ichni Official/Assets/Scripts/Story/Dialogue/VNPortraitStage.cs`
- **公共 API** `void ShowPortrait(string characterId, string slot, string emotion)``void HidePortrait(string characterId)``void HideAll()``void MovePortrait(string characterId, string slot, float duration)``void PlayEffect(string characterId, string effect, float intensity)``void HighlightSpeaker(string characterId)`
- **内部:** `Dictionary<string, IPortraitRenderer> activePortraits`;依据 `CharacterData.portraitStyle` 实例化对应 renderer 到槽位。
- **依赖:** A3、A9。
#### A11. `Story/Dialogue/StoryPresentationCommands.cs`(新建)
- **描述:** 表现类 Yarn 命令集合(`[YarnCommand]`)。路径 `/ichni Official/Assets/Scripts/Story/Dialogue/StoryPresentationCommands.cs`
- **命令集(完整表现集):** 均委托到 `VNPortraitStage`/`VNDialoguePresenter`
- `<<show_portrait characterId slot emotion>>` / `<<hide_portrait characterId>>` / `<<hide_all_portraits>>`
- `<<move_portrait characterId slot duration>>` / `<<jump_portrait characterId intensity>>` / `<<shake_portrait characterId intensity>>` / `<<set_emotion characterId emotion>>`
- `<<set_background bgId duration>>`(背景切换)
- `<<wait seconds>>`Yarn 内置也可,视需要)
- **注册方式:** 实例方法用 `[YarnCommand("show_portrait")]` 挂到与 `DialogueRunner` 同场景的组件上Yarn 自动发现),或在 `StoryDialogueController.Awake``dialogueRunner.AddCommandHandler(...)` 显式注册。**实现步骤时先核对 `DialogueRunner.ActionRegistration.cs``AddCommandHandler` 重载与 `[YarnCommand]` 自动注册要求。**
- **依赖:** A10、A12。
#### A12. `Story/Dialogue/StoryTreeCommands.cs`(新建)
- **描述:** 影响故事树/解锁的 Yarn 命令(替代旧 `StoryInterpreters``GenerateDialogBlock`/`GenerateSongBlock`/`SetUnlockKey`)。路径 `/ichni Official/Assets/Scripts/Story/Dialogue/StoryTreeCommands.cs`
- **命令集:**
- `<<unlock_block blockId>>``StoryTreeController.UnlockBlock`
- `<<insert_block blockId>>` → 从当前 block 生成后继("混合式"动态插入)→ `StoryTreeController.InsertBlock`
- `<<unlock_song songUnlockKey>>` → 复用旧 `SetUnlockKey` 逻辑:写入 `SongSaveModule.storyUnlockKeys`,并把"解锁提示消息"入队到对话结束后执行(见 A14 结束回调)。
- **依赖:** A6、A14、`GameSaveManager.SongSaveModule``ChapterSelectionManager`
#### A13. `Scripts/Saving/GameSaveManager.cs` → `StorySaveModule`(重建剧情存档模块)
- **描述:** 重建 `StorySaveModule`,适配新 DTO 与多类型变量。路径 `/ichni Official/Assets/Scripts/Saving/GameSaveManager.cs``StorySaveModule` 分部/内嵌类)。
- **改动:**
- block 存档改用 `ChapterStorySave`/`StoryBlockSave`A4ES3 存 `persistentDataPath/StorySaves/[Chapter].json`(沿用旧路径约定)。
- `storyVariables` 扩展为支持 Yarn 多类型float/bool/string——建议 `Dictionary<string, string>` 序列化 + 类型标记,或三个分字典;供 `StoryVariableStorage`A8读写。
- 保留 `selectedChoices` 语义Yarn 选项选择记录),如需回放已完成对话可保留(见"实现说明")。
- 保留 `SongSaveModule.storyUnlockKeys` 相关接口A12 依赖)。
- `ClearAllStoryline()` / `ClearStoryKeys()` 接口保留,供 `StoryManager.ClearAllStorySave` 调用。
- **注意:** `GameSaveManager` 含其它模块(`SongSaveModule`),改动**仅限剧情相关字段**,保持其余不变。**实现前先完整 view `GameSaveManager.cs``StorySaveModule` 现状。**
#### A14. `Story/Dialogue/StoryDialogueController.cs`(新建)
- **描述:** block → `DialogueRunner` 的桥接与生命周期协调。路径 `/ichni Official/Assets/Scripts/Story/Dialogue/StoryDialogueController.cs`
- **公共 API**
- `void PlayBlock(TextBlockView block)`:记录当前 block 上下文,`storyUIPage` 淡出/对话页淡入,`dialogueRunner.StartDialogue(block.yarnNodeName)`
- `List<UnityAction> dialogueEndActions`(保留旧 `dialogEndActions` 语义,供 A12 `unlock_song` 入队解锁提示)。
- **事件接线:** 订阅 `dialogueRunner.onDialogueComplete`:若当前 block 为 `Current``treeController.OnBlockCompleted(blockId)`;清空/执行 `dialogueEndActions`(弹解锁提示);`treeController.SaveChapter()`;对话页淡出、故事树页淡入。参考旧 `DialogManager.PlayDialog` 完成分支(旧 L138-152
- **依赖:** Yarn `DialogueRunner`、A6、A7、A12、`StoryUIPage`
#### A15. `Story/Dialogue/VNDialoguePresenter.cs`(新建)
- **描述:** VN 风格对话呈现器,继承 `Yarn.Unity.DialoguePresenterBase`。路径 `/ichni Official/Assets/Scripts/Story/Dialogue/VNDialoguePresenter.cs`
- **override**
- `OnDialogueStartedAsync()`显示对话面板DOTween 淡入)、`portraitStage` 就绪。
- `RunLineAsync(LocalizedLine line, LineCancellationToken token)`:从 `line.CharacterName``CharacterRegistry` 取显示名Unity Localization高亮说话者立绘`portraitStage.HighlightSpeaker`),打字机逐字显示 `line.TextWithoutCharacterName`;等待玩家点击推进(点击→`token` hurry/advance
- `RunOptionsAsync(DialogueOption[], LineCancellationToken)`:实例化选项按钮面板,等待选择,返回选中项;记录到 `selectedChoices`
- `OnDialogueCompleteAsync()`:对话面板淡出。
- **要点:** 全程 `YarnTask` 异步;打字机需响应 `LineCancellationToken`(点击加速/跳过)。**实现步骤时先 view `DialoguePresenterBase.cs``LocalizedLine`/`DialogueOption` 类型核对属性名(`CharacterName`/`TextWithoutCharacterName`/`Text`)。**
- **依赖:** A3、A10、Unity Localization。
#### A16. `Story/Dialogue/StoryFunctions.cs`(新建,可选)
- **描述:** `[YarnFunction]` 辅助,让 `.yarn``<<if>>` 能读取剧情状态。路径 `/ichni Official/Assets/Scripts/Story/Dialogue/StoryFunctions.cs`
- **示例:** `[YarnFunction("is_completed")] static bool IsCompleted(string blockId)``[YarnFunction("story_var")] static int StoryVar(string name)`
- **说明:** 多数条件可直接用 Yarn 变量(`$var`+ `StoryVariableStorage` 完成;此文件仅在需要读取"block 完成状态"等非变量信息时补充。
#### A17. 删除/弃用旧脚本
- **删除:** `Story/Dialog/DialogManager.cs``Story/Dialog/DialogModule.cs``Story/Dialog/StoryInterpreters.cs``Story/StoryUI/DialogUI/*``DialogUIPage`/`DialogContentFrame`/`DialogTextUI`/`ChoiceGroupUI`/`ChoiceButtonUI``Story/StoryUI/Blocks/*` 旧类(迁移到 A7 后删除);旧 `Story/StoryData/StoryData.cs`、旧 `Story/Dialog/StoryManager.cs`、旧 `Story/StoryUI/Storyline.cs`(被 A2/A5/A6 取代)。
- **注意:** DynamicExpresso 若无其它使用者,可保留包(供将来),但剧情不再依赖它。删除前 `grep` 确认无外部引用(尤其 `ChapterSelectionManager`/`Menu``DialogManager` 的调用点,需改为 `StoryDialogueController`)。
------
### B. Text Files`.yarn` 脚本)
#### B1. 示例章节 Yarn 脚本
- **描述:** 每个 TextBlock 对应一个 Yarn 节点。路径示例 `/ichni Official/Assets/Story/Chapters/Chapter1/Chapter1.yarn`(新目录,脱离旧 `Resources/Story/.../Dialogs`)。
- **内容:** 为若干 TextBlock 各写一个 `title:` 节点,含 speaker 行、`<<show_portrait>>`/`<<jump_portrait>>` 等表现命令、`->` 选项、`<<if $var>>` 条件分支、`<<set $var to ...>>`、必要处 `<<unlock_block>>`/`<<unlock_song>>`。用于验证管线。
- **依赖:** A11/A12 命令、A8 变量。
------
### C. Unity AssetsScriptableObject / YarnProject
#### C1. `Chapter1` YarnProject
- **名称:** `/ichni Official/Assets/Story/Chapters/Chapter1/Chapter1.yarnproject`
- **描述:** 编译 B1 的 `.yarn`,配置 **Localisation = Unity**(使用 `UnityLocalisedLineProvider`),关联 Unity Localization String Table Collection。
- **Bezi Actions** 无直接创建 YarnProject 的 Action → **手动创建**(见 E2
#### C2. `StoryData`Chapter1资产
- **名称:** `/ichni Official/Assets/Story/Chapters/Chapter1/Chapter1_StoryData.asset`
- **描述:** 定义 Chapter1 故事树 block、连接、初始状态、`yarnNodeName`、解锁条件,引用 C1 YarnProject。
- **Bezi Actions** `createOrUpdateScriptableObject``scriptableObjectType: "Ichni.Story.StoryData"`)。
- **依赖:** A2、C1。
#### C3. `CharacterRegistry` + 角色资产
- **名称:** `/ichni Official/Assets/Story/Characters/CharacterRegistry.asset`
- **描述:** 登记若干测试角色characterId、displayNameKey、emotion→Sprite
- **Bezi Actions** `createOrUpdateScriptableObject``scriptableObjectType: "Ichni.Story.CharacterRegistry"`)。
- **依赖:** A3。
------
### D. Unity Actions场景 / 预制体 / UI 接线,均在 `MenuScene.unity`
#### D1. Yarn 运行时对象
- **描述:** 在剧情 UI 层新建 `StoryDialogueRoot` GameObject`DialogueRunner` + `UnityLocalisedLineProvider` + `StoryVariableStorage` + `StoryDialogueController` + `StoryPresentationCommands` + `StoryTreeCommands``DialogueRunner.yarnProject` 指向 C1`dialogueViews` 加入 D2 的 `VNDialoguePresenter``variableStorage` 指向 `StoryVariableStorage``lineProvider` 指向 `UnityLocalisedLineProvider`
- **Bezi Actions** `createGameObject``addOrUpdateComponent`(逐个组件;`DialogueRunner` 字段引用用 `UnityObjectReference`)。
- **依赖:** A8/A11/A12/A14/A15、C1。
#### D2. VN 对话 UI
- **描述:** 构建 VN 对话面板:全屏遮罩/背景 `Image`、立绘舞台Left/Center/Right 槽位 `Image`)、对话框(说话者名 `TextMeshProUGUI` + 正文 `TextMeshProUGUI` + 继续指示)、选项面板(竖直布局,运行时实例化选项按钮)。挂 `VNDialoguePresenter``VNPortraitStage`,槽位挂 `SpritePortraitRenderer`
- **Bezi Actions** `createGameObject``addOrUpdateComponent``UnityEngine.UI.Image`/`TMPro.TextMeshProUGUI`/`UnityEngine.UI.Button`)、`setRectTransformLayout`(锚点/拉伸)。
- **依赖:** A9/A10/A15。
#### D3. 故事树 UI 与 block 预制体
- **描述:** 构建/复用故事树滚动视图(`ScrollRect content`);创建 `TextBlockView`/`SongBlockView`/`TutorialBlockView`/`BlockConnectorView` 预制体(含 `Button``inPort`/`outPort`、状态视觉),供 `StoryTreeController` 实例化。将 `StoryManager`/`StoryTreeController`/`StoryUIPage` 组件接线到既有剧情 UI 对象。
- **Bezi Actions** `createGameObject``addOrUpdateComponent``setRectTransformLayout``createPrefab`block 预制体)。
- **依赖:** A5/A6/A7、C2/C3。
------
### E. Manual User Actions
#### E1. 安装 Unity Localization规划弃用 I2
- **描述:** 通过 Package Manager 安装 `com.unity.localization`(使 `UnityLocalisedLineProvider.Installed.cs` 生效);创建 Locale 与 String Table Collection对话文本 + UI 文本。I2 的实际移除可后置,避免一次性破坏现有 UI 文本;本计划仅让**剧情对话**走 Unity Localization。
#### E2. 创建并配置 YarnProject
- **描述:** 在 `Assets/Story/Chapters/Chapter1/` 右键创建 YarnProject纳入 B1 的 `.yarn`Inspector 中设 Localisation 为 Unity 并关联 String Table Collection导入生成基础语言行。Bezi Actions 无 YarnProject 创建能力。)
#### E3. 核对 Yarn 命令/呈现器 API 签名
- **描述:** 实现 A8/A11/A15 前,在 Editor 中确认 `VariableStorageBehaviour``DialoguePresenterBase``LocalizedLine``DialogueOption``DialogueRunner.AddCommandHandler`/`[YarnCommand]` 的确切签名(版本 3.2.1),避免 API 漂移导致编译错误。
------
## Key Patterns to Reuse
- **Source:** `/ichni Official/Assets/Scripts/Story/StoryUI/Storyline.cs`**Pattern:** 故事树构建、连接线生成、按章节 ES3 存取、`ScrollRect` 布局,迁移到 `StoryTreeController`
- **Source:** `/ichni Official/Assets/Scripts/Story/Dialog/DialogManager.cs`L138-152 完成分支、`dialogEndActions`)— **Pattern:** 对话完成 → 置 block `Completed` → 执行结束回调 → 存档,迁移到 `StoryDialogueController.onDialogueComplete`
- **Source:** `/ichni Official/Assets/Scripts/Story/Dialog/StoryInterpreters.cs`**Pattern:** `SetUnlockKey` 的歌曲解锁 + 消息入队逻辑,迁移到 `StoryTreeCommands.unlock_song`
- **Source:** `SLSUtilities/General/Singleton<T>`**Pattern:** `StoryManager` 单例,替换手写 `instance = this`
- **Source:** 旧 `StoryData.cs` 的 Odin `[FoldoutGroup("$blockName")]`/`[InlineProperty]`**Pattern:**`StoryBlockDefinition` 的 Inspector 组织。
- **Source:** `DialogCharacter`(旧 `DialogModule.cs``name`/`title`/`emotions`)— **Pattern:** 演进为 `CharacterData``characterId`/`displayNameKey`/`emotionSprites`)。
------
## Implementation Notes
- **Yarn 3.x 异步:** Presenter 全部 `YarnTask`;打字机与"点击推进"须响应 `LineCancellationToken`hurry-up / cancel否则点击无法加速或跳过。
- **命令注册:** `[YarnCommand]` 自动发现要求承载组件与 `DialogueRunner` 处于可解析范围;若自动发现不稳定,改用 `DialogueRunner.AddCommandHandler` 显式注册(在 `StoryDialogueController.Awake`)。
- **变量类型:** Yarn 变量支持 number/bool/string`storyVariables``int`须扩展多类型存储A13否则 `StoryVariableStorage` 无法承载布尔/字符串。
- **本地化桥接:** speaker 名解析来自 Yarn 行的 `CharacterName`;显示名走 Unity Localization`CharacterData.displayNameKey`),正文走 `UnityLocalisedLineProvider` 关联的 String Table。
- **立绘抽象:** 不要在 Presenter/命令中直接写 `Image`;一律经 `IPortraitRenderer`,为 Live2D/Spine 预留。DOTween 已在项目中,用于跳动/移动/晃动。
- **混合式解锁:** 静态解锁在 `OnBlockCompleted` 重算 `UnlockCondition`;动态解锁由 `<<unlock_block>>`/`<<insert_block>>` 命令在对话中触发——两者都最终调用 `StoryTreeController`,保证存档一致。
- **存档兼容:** 这是破坏性重构,旧 `StorySaves/*.json`/`Choices.json` 结构不兼容;建议在 `StorySaveModule` 加版本号或提供一次性清档(`ClearAllStorySave` 已存在)。
- **场景范围:** 所有场景接线在 `MenuScene.unity`;剧情为 Official 独有,无需同步到 `IchniCreatorStudio`
- **可运行性分阶段:** 每阶段完成后项目应可编译、可进入剧情页。先落数据/树(占位对话),再接 Yarn 管线,再做 VN 呈现与命令,最后清理旧系统。
------
## TODO
**阶段 1 — 数据与故事树骨架(可编译、可显示占位树)**
-
新建 `StoryBlockDefinition.cs`block 定义 + `UnlockCondition`/`VariableCondition` + `StoryBlockType`
-
重建 `StoryData.cs`(章节 block 列表 + `YarnProject` 引用 + 查询 API
-
新建 `CharacterData.cs` + `CharacterRegistry.cs`(含 `portraitStyle` 预留)
-
重建 `StorySave.cs``StoryBlockSave`/`BlockConnectorSave`/`ChapterStorySave`/`StoryVariablesSave`
-
重建 `StoryManager.cs`(单例协调器 + `StoryBlockState`
-
重建 `StoryTreeController.cs`(构建/解锁重算/存取,从 `Storyline` 迁移)
-
重建 `StoryBlockView` + `TextBlockView`/`SongBlockView`/`TutorialBlockView`/`BlockConnectorView`
-
`createOrUpdateScriptableObject` 生成 `Chapter1_StoryData.asset``CharacterRegistry.asset`(用户已手动创建三个 SO 资产)
-
用 Bezi Actions 搭建故事树滚动视图与 block 预制体,接线 `StoryManager`/`StoryTreeController`/`StoryUIPage`(改为用户手动制作,见阶段总结的手动步骤)
-
(提前合并原阶段 5 清理)删除旧剧情/对话脚本网:`DialogManager`/`DialogModule`/`StoryInterpreters`/`Storyline`/旧 `StoryManager`/旧 Block UIs/`DialogUI/*`/旧 `StoryData`/旧 `StorySave``MenuManager` 移除失效 `dialogUIPage` 字段
**阶段 2 — Yarn 对话管线(点击文本块可跑通 Yarn 节点)**
-
安装 `com.unity.localization` 并创建 Locale / String Table Collection手动
-
手动创建 `Chapter0.yarnproject` 并设为 Unity Localisation编写示例 `Chapter0.yarn`
-
新建 `StoryVariableStorage.cs``VariableStorageBehaviour` 桥接存档,多类型变量)
-
重建 `GameSaveManager.StorySaveModule`(新 DTO + 多类型 `storyVariables`,保留 `SongSaveModule` 不变)——已在阶段 1 StorySave 步骤中一并完成:树+选项按章节(单 key 容器变量全局float/string/bool 三类型,匹配 Yarn 契约)
-
新建 `StoryDialogueController.cs``PlayBlock` + `onDialogueComplete` → 置 `Completed`/重算解锁/存档/结束回调)
-
`MenuScene` 搭建 `StoryDialogueRoot``DialogueRunner` + `StoryVariableStorage` + 控制器),接线到 `Chapter0.yarnproject`。注:项目未安装 `com.unity.localization``Chapter0.yarnproject` 使用 Yarn 内置本地化;`DialogueRunner` 在无 lineProvider 时会自动创建 `BuiltinLocalisedLineProvider`,故阶段 2 无需接 Unity Localization。呈现器dialoguePresenters留空待阶段 3 接入 VN 呈现器。
**阶段 3 — VN 呈现与立绘**
-
新建 `VNDialoguePresenter.cs``DialoguePresenterBase`:打字机行、说话者显示名、选项面板、淡入淡出,响应 `LineCancellationToken`
-
新建 `IPortraitRenderer.cs` + `SpritePortraitRenderer.cs`DOTween 跳动/移动/晃动)
-
新建 `VNPortraitStage.cs`(多槽位管理,按 `portraitStyle` 实例化 renderer
-
用 Bezi Actions 搭建 VN 对话 UI背景/立绘槽位/对话框/选项面板),接入 `DialogueRunner.dialogueViews`
**阶段 4 — 表现与树命令、条件**
-
新建 `StoryPresentationCommands.cs``show/hide/move/jump/shake_portrait``set_emotion``set_background`
-
新建 `StoryTreeCommands.cs``unlock_block`/`insert_block`/`unlock_song`,迁移旧 `SetUnlockKey` 歌曲解锁+消息入队)
-
新建 `StoryFunctions.cs``[YarnFunction]` `is_completed`/`story_var`,按需)
-
在示例 `.yarn` 中覆盖表现命令、选项、`<<if>>` 条件、`<<set>>``<<unlock_*>>` 并验证
**阶段 5 — 清理旧系统**
-
`grep` 确认并改接 `ChapterSelectionManager`/`Menu` 等对 `DialogManager` 的调用点到 `StoryDialogueController`
-
删除 `DialogManager.cs`/`DialogModule.cs`/`StoryInterpreters.cs`/`DialogUI/*`/旧 `Blocks/*`/旧 `Storyline.cs`/旧 `StoryData.cs`/旧 `StoryManager.cs`
-
重新编译并清零剧情相关编译错误

View File

@@ -0,0 +1,394 @@
# ichni Official / IchniCreatorStudio 技术对照文档
生成日期2026-07-08
本文档记录 `ichni Official``IchniCreatorStudio` 的当前结构、同步关系和关键技术差异,供后续任务开始前快速参考。本文档基于本地仓库扫描结果,不代表所有运行时行为已经通过 Unity Editor 实机验证。
## 1. 工作区与权限
| 项目 | 路径 | 当前角色 | 当前写入状态 |
| --- | --- | --- | --- |
| `ichni Official` | `D:\Projects\ichni Official` | 游戏本体 | 可读写 |
| `IchniCreatorStudio` | `D:\Projects\IchniCreatorStudio` | 关卡编辑器 / 内容制作工具 | 可读写 |
已完成的权限验证:
- `D:\Projects\IchniCreatorStudio` 已加入当前 Codex workspace writable roots。
- 已在 `D:\Projects\IchniCreatorStudio\.codex-write-test.tmp` 执行创建、读取、删除测试。
- 测试结果:`WRITE_RETEST_OK: codex write retest 2026-07-08`
## 2. 统一工作标准
后续所有工作继续遵循 `D:\Projects\ichni Official\AGENTS.md` 中记录的标准:
- 两个项目遵循统一编码规范。
- 文档和共享资源应供所有工作区成员查阅。
- `_BM` 后缀类是序列化存档类,本文档称为 `BM` 类。
- 新增或修改继承自 `BaseElement` 的游戏元素时,应优先同步检查两个项目。
- `ichni Official` 使用 `Wwise 2024.1.9.8920`
- `IchniCreatorStudio` 使用 Unity 原生音频。
- `ichni Official` 使用 `Yarn Spinner`,允许修改和扩展。
- 回复用户使用简体中文代码、API、类名、路径和特殊术语保留英文。
## 3. Unity 与 Package 基线
两个项目当前 Unity 版本一致,项目文件实际记录为:
- `6000.3.7f1 (696ec25a53d1)`
共同关键 packages
- `com.unity.inputsystem@1.18.0`
- `com.unity.render-pipelines.universal@17.3.0`
- `com.unity.timeline@1.8.10`
- `com.unity.test-framework@1.6.0`
- `com.unity.ugui@2.0.0`
- `com.unity.visualscripting@1.9.9`
- `com.unity.collections@2.6.2`
`ichni Official` 扫描到的 package
- `com.taptap.sdk.core@4.10.4`
- `com.taptap.sdk.login@4.10.4`
- `com.unity.burst@1.8.27`
- `com.unity.localization@1.5.12`
- `com.unity.nuget.newtonsoft-json@3.2.1`
`IchniCreatorStudio` 扫描到的 package
- `com.unity.burst@1.8.29`
- `com.unity.ide.vscode@1.2.5`
- `com.unity.memoryprofiler@1.1.9`
注意:两个项目的 `Burst` patch 版本不同。除非确实需要统一,否则不要为了版本一致性单独改 package。
## 4. 顶层结构对照
`ichni Official` 关键结构:
- `Assets/Scripts/DataCore`
- `Assets/Scripts/Game`
- `Assets/Scripts/GameUI`
- `Assets/Scripts/Generated`
- `Assets/Scripts/Manager`
- `Assets/Scripts/Menu`
- `Assets/Scripts/NewStorySystem`
- `Assets/Scripts/Online`
- `Assets/Scripts/Saving`
- `Assets/Scripts/Settings`
- `Assets/Scripts/SLSUtilities`
- `Assets/Story`
- `Assets/Wwise`
- `ichni Official_WwiseProject`
`IchniCreatorStudio` 关键结构:
- `Assets/Scripts/CommandSystem`
- `Assets/Scripts/Console`
- `Assets/Scripts/DynamicUI`
- `Assets/Scripts/Editor Tools`
- `Assets/Scripts/Extensions`
- `Assets/Scripts/Game/DataCore`
- `Assets/Scripts/Game/EditorCore`
- `Assets/Scripts/Game/RuntimeCore`
- `Assets/Scripts/Gird`
- `Assets/Scripts/Manager`
- `Assets/Scripts/Settings`
- `Assets/Scripts/SLSUtilities`
- `Assets/Scripts/StartMenu`
- `NodeScript`
脚本数量快照:
| 项目 | `Assets/Scripts` C# | BM C# | Runtime / Game 核心 C# |
| --- | ---: | ---: | ---: |
| `ichni Official` | 340 | 83 | `Assets/Scripts/Game`: 102 |
| `IchniCreatorStudio` | 401 | 87 | `Assets/Scripts/Game/RuntimeCore`: 108 |
## 5. 核心架构差异
### 5.1 游戏本体
`ichni Official` 的核心运行入口集中在:
- `Assets/Scripts/Manager/GameManager.cs`
- `Assets/Scripts/Manager/ProjectLoader.cs`
- `Assets/Scripts/Game`
- `Assets/Scripts/DataCore`
- `Assets/Scripts/NewStorySystem`
它更偏向最终运行时:
- 加载打包或导出的项目数据。
- 使用 `Wwise` 播放音乐和音效。
- 执行关卡、判定、镜头、视觉元素、故事和存档。
- 使用 `Yarn Spinner` 驱动剧情。
- 包含线上、登录、设置、解锁、剧情存档等玩家侧系统。
### 5.2 编辑器
`IchniCreatorStudio` 的核心编辑入口集中在:
- `Assets/Scripts/Manager/EditorManager.cs`
- `Assets/Scripts/Manager/ProjectManager.cs`
- `Assets/Scripts/Game/RuntimeCore`
- `Assets/Scripts/Game/DataCore`
- `Assets/Scripts/Game/EditorCore`
它更偏向内容制作和导出:
- 管理工程文件、歌曲文件、谱面、命令脚本。
- 在编辑器内复用大量 runtime 元素。
- 每个可编辑元素通常还需要配套 `EditorCore` 脚本。
- 使用 Unity 原生 `AudioSource` 和动态音频加载。
- 导出给 `ichni Official` 使用的压缩加密 `BM` 数据。
## 6. BM 与 BaseElement 同步关系
### 6.1 BM 覆盖情况
当前 BM 文件名对照结果:
- `ichni Official` BM 文件名数量100
- `IchniCreatorStudio` BM 文件名数量105
- `ichni Official` 中存在但 `IchniCreatorStudio` 中缺失0
- `IchniCreatorStudio` 中存在但 `ichni Official` 中缺失5
`IchniCreatorStudio` 额外存在的 BM
- `Custom2DShape_BM.cs`
- `DisplacementTracker_BM.cs`
- `ScaleTracker_BM.cs`
- `SwirlTracker_BM.cs`
- `Vector3Interferometer_BM.cs`
共同 BM 文件中,只有少数内容完全一致,大多数同名 BM 在两个项目中存在实现差异。这是正常现象,因为编辑器侧需要保存、复制、编辑器行为或导出行为,游戏本体侧需要执行和运行时加载。
重要规则:不要因为同名就直接覆盖 BM 文件。任何 BM 同步都应逐个比较字段、`ExecuteBM()``AfterExecute()``SaveBM()``DuplicateBM()` 和导出兼容性。
### 6.2 BaseElement 差异
`ichni Official`
- `Assets/Scripts/Game/Base/BaseElement.cs`
- `IBaseElement` 包含 `matchedBM``Refresh()``OnDelete()``Delete()``SetUpInspector()`
- `Assets/Scripts/Game/GameElements/Essential/GameElement.cs` 更偏运行时生命周期。
`IchniCreatorStudio`
- `Assets/Scripts/Game/RuntimeCore/Base/BaseElement.cs`
- `IBaseElement` 在运行时接口基础上额外包含 `SaveBM()`
- `Assets/Scripts/Game/RuntimeCore/GameElements/Essential/GameElement.cs` 额外有 `SetEditorSubmodules()`
- `Assets/Scripts/Game/DataCore/Base_BM/GameElement_BM.cs` 额外包含 `DuplicateBM(GameElement attached)`
新增 `BaseElement` 派生元素时,通常需要同时考虑:
- Official runtime script
- Official `BM` script
- Creator `RuntimeCore` script
- Creator `DataCore` / `BM` script
- Creator `EditorCore` inspector 或编辑交互脚本
- 主题包内对应 runtime、data、editor 三侧脚本
## 7. 存档、加载与导出
### 7.1 Official 加载
关键文件:
- `D:\Projects\ichni Official\Assets\Scripts\Manager\ProjectLoader.cs`
当前扫描到的核心设置:
- `SaveSettings`: `compressionType=None`, `encryptionType=None`, `format=JSON`, `location=Resources`
- `LoadSettings`: `compressionType=Gzip`, `encryptionType=AES`, `format=JSON`, `location=Resources`
主要加载对象:
- `ProjectInformation_BM`
- `SongInformation_BM`
- `BeatmapContainer_BM`
- `CommandScripts_BM`
加载后通过 `ExecuteBM()` 生成运行时对象。
### 7.2 Creator 保存与导出
关键文件:
- `D:\Projects\IchniCreatorStudio\Assets\Scripts\Manager\ProjectManager.cs`
当前扫描到的核心设置:
- `SaveSettings`: `compressionType=None`, `encryptionType=None`, `format=JSON`
- `ExportSettings`: `compressionType=Gzip`, `encryptionType=AES`, `format=JSON`
导出流程通常会:
- 调用 `SaveBM()`
- 写出 `ProjectInformation`
- 写出 `SongInformation`
- 写出 `Beatmap`
- 写出 `CommandScripts`
这说明 `IchniCreatorStudio` 是 Official 可加载资源的重要源头。改动序列化字段时,必须同时确认保存、导出和 Official 加载链路。
## 8. 音频系统差异
### 8.1 Official: Wwise
Official 使用:
- `Assets/Wwise`
- `ichni Official_WwiseProject`
- `Assets/Scripts/Manager/SongPlayer.cs`
- `Assets/Scripts/SLSUtilities/WwiseAssistance/AudioManager.cs`
Wwise 版本:
- Wwise SDK `2024.1.9 Build 8920`
- Unity Integration Bundle `2024.1.9.3788`
Official 音频特点:
- 使用 `AK.Wwise.Event``RTPC``AkUnitySoundEngine`
- `SongPlayer` 通过 Wwise event 播放音乐,并读取 segment position。
- `LowPassFilterEffect` / `HighPassFilterEffect` 通过 Wwise RTPC 控制滤波效果。
- 音频事件、RTPC、bank 和 Unity 侧引用需要保持一致。
### 8.2 Creator: Unity Native Audio
Creator 使用:
- `Assets/Scripts/Manager/MusicPlayer.cs`
- `Assets/Scripts/Manager/EditorManager.cs`
- `Assets/Scripts/Game/RuntimeCore/Base/ProjectFiles/SongInformation.cs`
- `Assets/Scripts/Game/RuntimeCore/Base/ProjectFiles/NoteAudioCollection.cs`
Creator 音频特点:
- 使用 `AudioSource``AudioClip``AudioMixerGroup`
- `SongInformation` 负责加载音频文件。
- 包含自定义 `LoadMP3``LoadWav` 流程。
- `MusicPlayer` 实现 `ISongTimeProvider`,供编辑器时间轴使用。
音频相关游戏元素或效果不能直接从 Official 复制到 Creator。Official 侧通常要接 WwiseCreator 侧通常要接 Unity 原生音频和编辑器预览。
## 9. 剧情系统
`ichni Official` 使用 `Yarn Spinner`,主要集中在:
- `D:\Projects\ichni Official\Assets\Story`
- `D:\Projects\ichni Official\Assets\Scripts\NewStorySystem`
已扫描到的关键文件:
- `Assets/Story/Chapter0/Chapter0.yarnproject`
- `Assets/Story/Chapter0/C0_A0.yarn`
- `Assets/Story/Chapter0/StoryData_Chapter0.asset`
- `Assets/Story/Chapter0/CharacterRegistry.asset`
- `Assets/Scripts/NewStorySystem/StoryDialogueController.cs`
- `Assets/Scripts/NewStorySystem/YarnFunctions/VariableFunctions.cs`
- `Assets/Scripts/NewStorySystem/YarnFunctions/StoryTreeCommands.cs`
- `Assets/Scripts/NewStorySystem/YarnFunctions/PortraitCommands.cs`
当前 `Chapter0.yarnproject` 关键信息:
- `projectFileVersion`: 4
- `sourceFiles`: `**/*.yarn`
- `baseLanguage`: `en`
- 已配置 `zh-CN` localisation
- 当前 editor options 中可见角色:`SLS``Asahi`
`IchniCreatorStudio` 当前没有扫描到对应 Yarn 资源。剧情系统修改一般只影响 Official除非未来编辑器需要制作剧情内容。
## 10. ThemeBundle 与元素扩展
两个项目都包含主题包结构,例如:
- `Basic`
- `DepartureToMultiverse`
- `MetropolisOnOrbit`
- `Shapes`
主题包脚本通常按职责拆分:
- Official: runtime + data/BM
- Creator: runtime + data/BM + editor
示例:
- Official `DepartureToMultiverse` 包含 `DTMTrail.cs``DTMStarrySkybox.cs``DTMRandomGridFloor.cs` 等 runtime 脚本,以及对应 `*_BM.cs`
- Creator `DepartureToMultiverse` 除 runtime 和 `*_BM.cs` 外,还包含 `DTMTrail_Editor.cs``DTMStarrySkybox_Editor.cs``DTMRandomGridFloor_Editor.cs` 等编辑器脚本。
- Creator `Basic` 中存在 `Custom2DShape_BM.cs``Custom2DShape_Editor.cs`,但 Official 当前没有同名 BM 文件。
新增主题包元素时,应先找同主题下已有元素作为模板,再分别实现 Official runtime/BM 与 Creator runtime/BM/editor。
## 11. 当前同步风险与注意事项
1. BM 文件名高度重合,但内容大多不一致。
- 不能机械同步。
- 字段变更必须评估导出兼容性和旧数据加载。
2. Creator 覆盖了所有 Official BM 文件名,并多出 5 个 BM。
- 如果 Official 后续需要加载这些 Creator-only 元素Official 需要新增对应 runtime/BM 支持。
3. Official 与 Creator 的音频系统不同。
- Official: `Wwise`
- Creator: Unity native audio
- 音频效果和播放逻辑必须双实现或抽象到共同时间接口。
4. Official 有剧情系统Creator 当前没有。
- 修改 `Yarn Spinner``NewStorySystem` 前不应假设 Creator 有对应编辑器支持。
5. Creator 有 `EditorCore`Official 没有。
- 元素编辑能力通常不能只靠 runtime/BM 实现。
6. 当前两个仓库均存在工作区未提交或未跟踪文件。
- 后续修改前应先检查 `git status`
- 不要还原或覆盖用户已有改动。
## 12. 后续任务检查清单
修改或新增 `BaseElement` / 游戏元素时:
- 检查 Official runtime 脚本。
- 检查 Official `BM` 脚本。
- 检查 Creator `RuntimeCore` 脚本。
- 检查 Creator `DataCore` / `BM` 脚本。
- 检查 Creator `EditorCore` 脚本。
- 检查主题包内是否有同类元素模板。
- 检查 `SaveBM()``ExecuteBM()``AfterExecute()``DuplicateBM()`
- 检查旧数据加载和导出兼容性。
- 如果涉及音频,分别实现 Wwise 侧和 Unity native audio 侧。
- 如果涉及剧情,只默认影响 Official除非明确要求 Creator 支持剧情编辑。
修改 BM 字段时:
- 确认字段默认值。
- 确认序列化名称和旧文件兼容性。
- 确认 Creator 保存和导出。
- 确认 Official 加载和执行。
- 尽量准备一个最小导出样例进行回归验证。
修改 package、Unity 版本或项目设置时:
- 不要只改一个项目,除非差异有明确原因。
- 先确认 `Packages/manifest.json``ProjectSettings/ProjectVersion.txt`
- 记录差异原因。
## 13. 关键文件索引
| 主题 | Official | Creator |
| --- | --- | --- |
| 工作标准 | `D:\Projects\ichni Official\AGENTS.md` | `D:\Projects\IchniCreatorStudio\AGENTS.md` |
| Unity 版本 | `D:\Projects\ichni Official\ProjectSettings\ProjectVersion.txt` | `D:\Projects\IchniCreatorStudio\ProjectSettings\ProjectVersion.txt` |
| Packages | `D:\Projects\ichni Official\Packages\manifest.json` | `D:\Projects\IchniCreatorStudio\Packages\manifest.json` |
| Runtime elements | `D:\Projects\ichni Official\Assets\Scripts\Game` | `D:\Projects\IchniCreatorStudio\Assets\Scripts\Game\RuntimeCore` |
| BM data | `D:\Projects\ichni Official\Assets\Scripts\DataCore` | `D:\Projects\IchniCreatorStudio\Assets\Scripts\Game\DataCore` |
| Editor scripts | 无统一 `EditorCore` | `D:\Projects\IchniCreatorStudio\Assets\Scripts\Game\EditorCore` |
| Official loader | `D:\Projects\ichni Official\Assets\Scripts\Manager\ProjectLoader.cs` | 不适用 |
| Creator save/export | 不适用 | `D:\Projects\IchniCreatorStudio\Assets\Scripts\Manager\ProjectManager.cs` |
| Official audio | `D:\Projects\ichni Official\Assets\Scripts\Manager\SongPlayer.cs` | 不适用 |
| Creator audio | 不适用 | `D:\Projects\IchniCreatorStudio\Assets\Scripts\Manager\MusicPlayer.cs` |
| Story/Yarn | `D:\Projects\ichni Official\Assets\Scripts\NewStorySystem` | 当前无对应系统 |

13
docs/work-standards.md Normal file
View File

@@ -0,0 +1,13 @@
# Ichni 工作区永久工作标准
本文档面向所有工作区成员,记录 `ichni Official``ichniCreatorStudio` 的统一协作标准。除非用户明确要求修改工作标准,否则不得擅自变更这些标准。
1. 本工作区内的所有项目均遵循统一的编码规范。
2. 文档及共享资源应供所有工作区成员查阅。
3. `ichni Official` 是游戏本体项目;`ichniCreatorStudio` 位于 `D:\Projects\IchniCreatorStudio`,是游戏的关卡编辑器。二者遵循相似的项目结构和脚本结构,并共享序列化存档类。类名后缀为 `_BM` 的类称为 `BM` 类。
4. 当需要编写新的游戏元素,也就是继承自 `BaseElement` 的元素,或修改现有游戏元素脚本时,如果条件允许,需要同时在 `ichni Official``ichniCreatorStudio` 中编写或修改适合对应项目的脚本,并提醒用户跨项目同步情况。
5. `ichni Official` 使用 `Wwise 2024.1.9.8920` 作为音频中间件。
6. `ichniCreatorStudio` 因为需要自定义音频加载,使用 Unity 原生音频。
7. `ichni Official` 使用 `Yarn Spinner` 制作游戏剧情。允许修改和扩展 `Yarn Spinner`
8. 与用户沟通时使用简体中文。代码、关键词、API 名称、类名、路径、特殊术语等需要使用英文时,应保留英文。