@fenglimg/cocos-state-controller 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +287 -0
- package/assets/script/controller/Capability.ts +100 -0
- package/assets/script/controller/Capability.ts.meta +10 -0
- package/assets/script/controller/CapabilityRegistry.ts +116 -0
- package/assets/script/controller/CapabilityRegistry.ts.meta +10 -0
- package/assets/script/controller/EnumPropRefMap.ts +232 -0
- package/assets/script/controller/EnumPropRefMap.ts.meta +10 -0
- package/assets/script/controller/NestedCtrlData.ts +199 -0
- package/assets/script/controller/NestedCtrlData.ts.meta +10 -0
- package/assets/script/controller/PrefabIntrospection.ts +151 -0
- package/assets/script/controller/PrefabIntrospection.ts.meta +10 -0
- package/assets/script/controller/Props.meta +13 -0
- package/assets/script/controller/StateControllerV2.ts +1957 -0
- package/assets/script/controller/StateControllerV2.ts.meta +10 -0
- package/assets/script/controller/StateEnumV2.ts +165 -0
- package/assets/script/controller/StateEnumV2.ts.meta +10 -0
- package/assets/script/controller/StateErrorManagerV2.ts +217 -0
- package/assets/script/controller/StateErrorManagerV2.ts.meta +10 -0
- package/assets/script/controller/StatePropHandlerV2.ts +316 -0
- package/assets/script/controller/StatePropHandlerV2.ts.meta +10 -0
- package/assets/script/controller/StatePropertyControlService.ts +148 -0
- package/assets/script/controller/StatePropertyControlService.ts.meta +10 -0
- package/assets/script/controller/StateSelectV2.ts +4542 -0
- package/assets/script/controller/StateSelectV2.ts.meta +10 -0
- package/assets/script/controller/capabilities/AutoSyncCapability.ts +30 -0
- package/assets/script/controller/capabilities/AutoSyncCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/EventCapability.ts +144 -0
- package/assets/script/controller/capabilities/EventCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/MigrationCapability.ts +94 -0
- package/assets/script/controller/capabilities/MigrationCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/MultiCtrlBindingCapability.ts +157 -0
- package/assets/script/controller/capabilities/MultiCtrlBindingCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/PropertyControlCapability.ts +124 -0
- package/assets/script/controller/capabilities/PropertyControlCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/RecordingCapability.ts +69 -0
- package/assets/script/controller/capabilities/RecordingCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/SelectedPageIdCapability.ts +88 -0
- package/assets/script/controller/capabilities/SelectedPageIdCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities.meta +13 -0
- package/assets/script/controller/props/CtrlInspectorGroups.ts +138 -0
- package/assets/script/controller/props/CtrlInspectorGroups.ts.meta +10 -0
- package/assets/script/controller/props/SelectInspectorGroups.ts +104 -0
- package/assets/script/controller/props/SelectInspectorGroups.ts.meta +10 -0
- package/bin/csc.js +286 -0
- package/package.json +60 -0
- package/packages/state-controller-v2-panel/README.md +80 -0
- package/packages/state-controller-v2-panel/inspector-inject.js +917 -0
- package/packages/state-controller-v2-panel/inspector-probe.json +3767 -0
- package/packages/state-controller-v2-panel/lib/handlers.js +534 -0
- package/packages/state-controller-v2-panel/main.js +149 -0
- package/packages/state-controller-v2-panel/package.json +32 -0
- package/packages/state-controller-v2-panel/panel/build.js +23 -0
- package/packages/state-controller-v2-panel/panel/logic.js +1207 -0
- package/packages/state-controller-v2-panel/panel/styles.css +454 -0
- package/packages/state-controller-v2-panel/panel/template.html +296 -0
- package/packages/state-controller-v2-panel/scene-accessor.js +657 -0
- package/skills/cocos-state-controller/SKILL.md +28 -0
- package/skills/cocos-state-controller/refs/cli-usage.md +78 -0
- package/skills/cocos-state-controller/refs/editor-guide.md +127 -0
- package/skills/cocos-state-controller/refs/migrate.md +106 -0
- package/skills/cocos-state-controller/refs/upstream-pr.md +66 -0
- package/tools/migration/migrate-prefab-v1-to-v2.js +608 -0
- package/tools/state-controller-sync-manifest.json +33 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cocos-state-controller
|
|
3
|
+
description: 安装/更新/迁移/检查/上行同步 StateControllerV2·StateSelectV2 与 csc CLI,以及编辑器里的录制/回收站/@property 排除/面板操作。涉及 StateController、StateSelect、状态控制器、csc、状态录制、属性排除 时使用;问某段 API 或内部原理时直接读源码。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cocos State Controller — 总入口
|
|
7
|
+
|
|
8
|
+
`@fenglimg/cocos-state-controller` 的统一入口。**按用户意图分诊到对应 `refs/` 文档,只读需要的那一个**,不要一次性加载全部。
|
|
9
|
+
|
|
10
|
+
## 跨域红线(任何操作先守)
|
|
11
|
+
|
|
12
|
+
- **写入前必 dry-run**:迁移、update、批量改写一律先看 dry-run 结果,确认无误再写。
|
|
13
|
+
- **不动 canonical `.meta` uuid**:`.meta` 承载 Cocos 资源 uuid,包独占固定,绝不让 Cocos 重新生成(否则引用全断)。
|
|
14
|
+
- **dirty worktree 先确认归属**:动手前确认现有改动是否属于状态控制器产物,不覆盖无关改动。
|
|
15
|
+
|
|
16
|
+
## 意图分诊
|
|
17
|
+
|
|
18
|
+
| 用户意图 | 读哪个 ref |
|
|
19
|
+
|---|---|
|
|
20
|
+
| 用 `csc` CLI:install / update / diff / doctor / sync / migrate / skill install / uninstall | `refs/cli-usage.md` |
|
|
21
|
+
| 在编辑器里操作:录制状态 / 移入回收站 / `@property` 纳入排除 / 用面板(state-controller-v2-panel) | `refs/editor-guide.md` |
|
|
22
|
+
| 把 v1 `StateController` / prefab 迁移到 V2 | `refs/migrate.md` |
|
|
23
|
+
| 把 consumer 工程的本地改动回流到源仓开 PR(上行同步) | `refs/upstream-pr.md` |
|
|
24
|
+
| **某段 API 怎么调 / 内部机制是什么原理** | **不查 ref —— 直接读源码**:业务切换 API 看 `assets/script/controller/capabilities/SelectedPageIdCapability.ts`;能力与原理看 `assets/script/controller/capabilities/*.ts` 与 `StateControllerV2.ts`(392 个 JSDoc 块,**代码是唯一真源**,文档不另存副本以免漂移) |
|
|
25
|
+
|
|
26
|
+
## ref 与版本
|
|
27
|
+
|
|
28
|
+
`refs/` 随本包版本走——与代码同 repo,打 `v*` tag 时一起快照;`csc skill install` 分发的就是该版本的 ref。**改 CLI / 面板 / 迁移行为时,必须连带更新对应 ref**,否则 ref 会滞后于代码。
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# csc CLI 用法
|
|
2
|
+
|
|
3
|
+
`@fenglimg/cocos-state-controller` 全局 CLI。命令真源 = `bin/csc.js`(拿不准时读它的 `COMMANDS`)。
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i -g @fenglimg/cocos-state-controller # 全局装
|
|
7
|
+
csc --help # 列全部命令
|
|
8
|
+
csc --version
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
CLI = 确定性机械活;需要看懂代码做判断的活(迁移适配、上行取舍、开 PR)交给本 skill 的其它 ref。
|
|
12
|
+
|
|
13
|
+
## install — 安装净荷到当前工程
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
csc install [--version X] [--runtime-path P] [--panel-path P]
|
|
17
|
+
```
|
|
18
|
+
- 拷 runtime(`assets/script/controller`)+ panel(`packages/state-controller-v2-panel`)+ **写 canonical `.meta`** + 写 `.csc/lock.json`。
|
|
19
|
+
- 装前做 **uuid 撞车预检**:consumer 异文件占用了包的 canonical uuid → **中止报红,不改任何文件**,提示用户重生成本地冲突方的 uuid(绝不动包的)。
|
|
20
|
+
- Cocos 版本不在 2.4.x **只 warn 不拦**。
|
|
21
|
+
- 装完提示「重启 Cocos 编辑器加载面板」。
|
|
22
|
+
- `--runtime-path` / `--panel-path`:自定义安装位置(记进 `.csc/lock.json`,`lock.files` 的 key 仍是 canonical 路径)。
|
|
23
|
+
|
|
24
|
+
## update — 更新到新版(三方合并)
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
csc update [--version Y]
|
|
28
|
+
```
|
|
29
|
+
逐 managed 文件:指纹 == lock(没动过)→ 直接覆盖;指纹 != lock(本地改过)→ 三方合并(base=装的版本 / ours=本地 / theirs=新版),无冲突自动并,**冲突打 `<<<<<<<` 标记**留人/AI 解。完后更新 `lock.json`。
|
|
30
|
+
|
|
31
|
+
## diff — 看本地相对装的版本改了什么
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
csc diff
|
|
35
|
+
```
|
|
36
|
+
列 `+新增 / -删除 / ~修改`(归一化后比对,行尾/平台差异不误报)。
|
|
37
|
+
|
|
38
|
+
## doctor — 体检
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
csc doctor
|
|
42
|
+
```
|
|
43
|
+
检查:文件齐全 / lock 一致(本地漂移=上行候选,warn) / uuid 撞车(异文件同 uuid=fail) / Cocos 版本(2.4.x) / V1 cid 残留(迁移域,目前 skipped)。无 fail 即通过。
|
|
44
|
+
|
|
45
|
+
## sync --upstream — 算上行差异
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
csc sync --upstream [--output patch.diff]
|
|
49
|
+
```
|
|
50
|
+
重建装的版本 pristine 基线 + 反归一化到源仓 canonical 路径 + 三方 diff → 输出 **unified patch**(canonical 路径标签,落源仓布局)。范围只 managed runtime+panel,不含 tests。patch 交「上行 PR」流程处理(见 `refs/upstream-pr.md`)。
|
|
51
|
+
|
|
52
|
+
## migrate — prefab V1→V2 迁移
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
csc migrate <prefab|dir...> [--write] [--backup] [--allow-remote]
|
|
56
|
+
```
|
|
57
|
+
确定性迁移引擎。默认 dry-run,`--write` 写入,`--backup` 写前备份。**remote bundle 默认拒绝**(迁 V2-cid 会崩仅 V1 runtime 的老客户端),`--allow-remote` 显式放行。完整迁移工程流程见 `refs/migrate.md`(CLI 只是其中的机械步骤)。
|
|
58
|
+
|
|
59
|
+
## skill install — 分发 skills
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
csc skill install [--target claude|codex|all]
|
|
63
|
+
```
|
|
64
|
+
把包内 skills 分发到 `.claude/skills` + `.codex/skills`(`--target` 过滤)。
|
|
65
|
+
|
|
66
|
+
## uninstall — 回退
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
csc uninstall
|
|
70
|
+
```
|
|
71
|
+
按 lock 移除 managed 文件 + `.csc/`。
|
|
72
|
+
|
|
73
|
+
## 典型工作流
|
|
74
|
+
|
|
75
|
+
- **接入新工程**:`csc install` → 重启编辑器 → `csc doctor` 确认全绿。
|
|
76
|
+
- **升级版本**:`csc diff`(看本地有无改动)→ `csc update` → 解冲突 → `csc doctor`。
|
|
77
|
+
- **回流改动**:`csc diff` → `csc sync --upstream --output up.patch` → 走 `refs/upstream-pr.md` 开 PR。
|
|
78
|
+
- **迁移老活动**:见 `refs/migrate.md`(用到 `csc migrate`)。
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# 编辑器操作引导
|
|
2
|
+
|
|
3
|
+
用户在 Cocos Creator 2.4 编辑器里怎么操作 `StateControllerV2` / `StateSelectV2`。
|
|
4
|
+
**每节末尾的「真源」指向代码 `file:函数` 与测试**——回答用户时若 UI 细节拿不准,去读真源核实,别凭记忆编造点击路径(编辑器 UI 是最容易幻觉的地方)。
|
|
5
|
+
|
|
6
|
+
> 内容随包版本走;改了面板/组件行为要连带更新本文。inspector DOM 注入与面板交互**无 jest 覆盖,须真编辑器 e2e 验证**。
|
|
7
|
+
|
|
8
|
+
## 两个操作界面
|
|
9
|
+
|
|
10
|
+
| 界面 | 入口 | 适合 |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| **inspector 折叠组** | 选中挂了 StateControllerV2 / StateSelectV2 的节点,看属性检查器 | 单组件就近操作(录制、排除、状态操作、回收站、值搬运) |
|
|
13
|
+
| **state-controller-v2-panel 面板** | 编辑器面板菜单打开 | 全场景观测、跨控制器联动、值矩阵总览 |
|
|
14
|
+
|
|
15
|
+
很多操作两边都能做(录制、状态 CRUD、回收站)。下面按**操作主题**讲,标注每个界面怎么走。
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 面板三视图(state-controller-v2-panel)
|
|
20
|
+
|
|
21
|
+
面板根 `#app.panel-shell`,顶部三个 tab 切换(`logic.js:switchView`):
|
|
22
|
+
|
|
23
|
+
- **总览 `#view-overview`** —— 全场景控制器观测:仪表盘(每控制器当前状态 `renderDashboard`)、拓扑树(控制器→成员节点→受控属性 `renderTopology`,属性按 tracked/loose/excluded/mixed 着 `kind-badge`)、值矩阵(属性×状态 `renderMatrix`,点状态列头切状态、点属性名开详情抽屉)、异常清单(`renderIssues` 列 loose/excluded/mixed,一键定位)。编辑器里选中场景节点 → 总览自动展开滚到对应成员(`_reverseHighlight`)。
|
|
24
|
+
- **编辑 `#view-editor`** —— 单控制器操作:控制器选择(`#ctrl-switch-select` 下拉 / 前后箭头 `setCurrentCtrl`)、状态选择(`#state-pick-select` / 侧栏 `#states-list` → `_goState`)、新增/删除状态、录制三连、编辑期值矩阵 + 取消跟随、回收站、Inspector 增强开关。
|
|
25
|
+
- **联动 `#view-bindings`** —— 跨控制器联动:表单四下拉(源控制器/源状态/目标控制器/目标状态)+「+ 添加联动」(`_addBindingFromForm`);关系图按源控制器分组列边,「✕」删边(`_removeBinding`)。
|
|
26
|
+
|
|
27
|
+
面板↔场景全走 `_callScene(method, payload, cb)`(`logic.js:134` → `Editor.Scene.callSceneScript` → `scene-accessor.js` 同名 handler → `lib/handlers.js` 纯函数)。写操作后场景广播 `on-data-changed` + `scene:set-dirty`,面板自动刷新。
|
|
28
|
+
|
|
29
|
+
**真源**:`panel/logic.js`、`panel/template.html`、`lib/handlers.js`、`scene-accessor.js`;测试 `tests/panel/handlers.test.ts`、`inspectorStateValues.test.ts`、`sceneAccessor.smoke.test.ts`。
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 录制状态
|
|
34
|
+
|
|
35
|
+
把"在节点上直接拖属性"的改动录进当前 state,免手填。
|
|
36
|
+
|
|
37
|
+
- **inspector**:StateControllerV2/StateSelectV2 的「🔴 录制」折叠组点录制开关(布尔触发器,点亮=录制中,再点=停止)。两个组件共享同一录制态。
|
|
38
|
+
- **面板(编辑视图)**:`.record-actions` 的 `#btn-start-record`「🔴 录制」/ `#btn-stop-record`「⏹ 停止并保存」/ `#btn-cancel-record`「⤺ 撤销本次录制」。
|
|
39
|
+
- **进录制**:若已跟随 prop 与当前 state 存值不一致,弹窗三选一(保存到当前 state / 丢弃恢复 / 取消)。
|
|
40
|
+
- **录制中**:改已跟随 prop 即被记录;走 prefab diff 路径(不靠 cc 事件),`button.interactable`/`label.string`/`widget.top` 等无事件 prop 也能录。
|
|
41
|
+
- **停止**:commit 进当前 state;若有"录制期间改了但未跟随"的 prop,弹窗问是否追加跟随。
|
|
42
|
+
- **切 state 自动停录**:录制中直接切 state,会先把改动 commit 到 fromState 再切(静默,不弹窗)。
|
|
43
|
+
- **撤销录制**:面板有 `#btn-cancel-record`;inspector 折叠组**已移除取消按钮**,改用编辑器原生 `Ctrl+Z` 撤销整次录制。
|
|
44
|
+
|
|
45
|
+
**注意/不变量**:`_recording` 不序列化,重开编辑器后回 false;录制中**禁止改状态结构**(增删/重排/改名/复制/删除当前状态都被拒并 warn,需先停录);停止与取消时**被排除的 prop 都还原到录制前**(排除=录制期间完全不影响)。
|
|
46
|
+
|
|
47
|
+
**真源**:`StateControllerV2.ts:recordTrigger`(setter)/`startRecording`/`stopRecording`/`cancelRecording`、`StateSelectV2.ts:onRecordingStop`/`commitRecordingDiff`;`RecordingCapability.ts`(对外查询/广播 hook);`handlers.js:set-recording`/`cancel-recording`。测试 `tests/core/Recording.*.test.ts`(controller/snapshot/modelZ/fallbackCommit/cancel/cancelExcludedDirty/noEventProps/stateSwitch.e2e)、`Capability.recording.test.ts`。
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 状态 CRUD
|
|
52
|
+
|
|
53
|
+
- **新增**:inspector states 数组点 `+` / 面板编辑视图 `#btn-add-state`「+ 新增状态」。新项自动智能命名 + 分配自增 stateId(全程唯一,重名加 `_i` 后缀)。
|
|
54
|
+
- **删除(软删→回收站)**:inspector「状态操作」组"删除当前状态" / states 数组项 `×` / 面板状态条目 `.btn-del-state`「✕」(需 confirm)。删除是**软删**:移出活跃列表但 stateId 数据保留进回收站,可恢复。**至少保留一个状态**(length≤1 拒删)。
|
|
55
|
+
- **重命名**:inspector states 数组直接编辑 name;改回默认名(index+1)会清历史命名。
|
|
56
|
+
- **复制**:inspector「状态操作」组"复制当前状态"。新名 `<原名>_copy`,分配新 stateId,深拷属性数据并选中新状态。
|
|
57
|
+
- **上移/下移**:inspector「状态操作」折叠组(`moveStateUp`/`moveStateDown`)。
|
|
58
|
+
|
|
59
|
+
**真源**:`StateControllerV2.ts` states setter / `removeSelectedState` / `copySelectedState` / `adjustSelectedStateOrder` / `ensureUniqueStateIds`;`handlers.js:add-state`/`remove-state`。测试 `tests/core/StateController.crud.test.ts`/`deleteState.test.ts`/`copyState.test.ts`/`recycleBin.test.ts`、`StateSelect.copyClonePreservesType.test.ts`、`handlers.test.ts`。
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 回收站(软删状态暂存区)
|
|
64
|
+
|
|
65
|
+
- **入口**:inspector StateControllerV2「回收站」折叠组 / 面板编辑视图侧栏 `#recycle-bin`。
|
|
66
|
+
- **恢复**:选回收项 → 追加到状态列表尾部并选中(数据以 stateId 寻址,软删时从未清理,恢复即接回 `_ctrlData[stateId]`)。
|
|
67
|
+
- **彻底删除/清空**:硬删,**弹窗二次确认**(cancelId 默认取消防误删)。硬删后即使恢复同 stateId 数据也不复活。
|
|
68
|
+
- **只读预览**:恢复前预览回收态长什么样(叠加到节点显示,不改 selectedIndex/不标脏);切激活态/录制/销毁/切场景等任何出口都先精确还原;录制中不允许进入预览。
|
|
69
|
+
|
|
70
|
+
**真源**:`StateControllerV2.ts:recycleRestorePick`/`restoreDeletedState`/`recyclePurgePick`/`purgeDeletedState`/`recyclePurgeAll`/`recyclePreviewPick`/`previewDeletedState`/`exitPreview`;`handlers.js:restore-deleted-state`/`purge-deleted-state`/`purge-all-deleted-states`/`preview-deleted-state`/`exit-preview`。测试 `tests/core/StateController.recycleBin.test.ts`、`handlers.test.ts`(回收站系列)。
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## @property 跟随与排除
|
|
75
|
+
|
|
76
|
+
控制哪些属性随状态切换变化。
|
|
77
|
+
|
|
78
|
+
- **批量纳入**:`autoConfigureAllProperties()` 一次性启用节点上所有可跟踪 prop(`scanAvailableProperties` 判定可用性;不列 `Non(0)` 占位枚举,空白节点恒列 8 个 cc.Node 基础 prop)。
|
|
79
|
+
- **排除某 prop**:两条入口——(1) inspector StateSelectV2「排除管理」折叠组的 `+ 添加排除` 下拉(从当前跟随中选一个,选中即加入排除清单);(2) inspector 属性行左侧灰方块徽标上点击发 `inspector-toggle-exclude`。排除后该 prop 值不再随 state 变、不进 ctrlData。
|
|
80
|
+
- **恢复跟随**:「排除管理」的用户排除清单数组用 cocos 原生 `-` 删项(删项=重新跟随,别直接编辑文本);或已排除行徽标再点一次 unexclude。`SYSTEM_EXCLUDE` 部分不可恢复(只读展示)。
|
|
81
|
+
- **面板取消跟随**:编辑视图值矩阵行尾 `.em-cancel`「✕」→ `remove-property`。面板**没有**手动"+ 添加属性"按钮(`add-property` handler 存在但 UI 只通过录制自动跟随)。
|
|
82
|
+
|
|
83
|
+
**注意/不变量**:受控标记写在历史命名空间 `$$controlledProps$$`、用户排除存 `_userExcludedProps`(不改 key/路径,避免老 scene 反序列化丢失);`addExcludeTrigger` 的动态下拉选项用 `setClassAttr` 注入到**类**(`SelectExcludeGroup`)而非实例;`setPropExcluded` 幂等;`cc.Node.x` 等内置 propRef 同样可排除/恢复。
|
|
84
|
+
|
|
85
|
+
**真源**:`StateSelectV2.ts:autoConfigureAllProperties`/`togglePropertyControl`/`setPropExcluded`/`reconcileUserExcluded`/`excludedPropsDisplay`、`PropertyControlCapability.ts:scanAvailableProperties`、`props/SelectInspectorGroups.ts:SelectExcludeGroup`;`inspector-inject.js:onClick`、`scene-accessor.js:inspector-toggle-exclude`、`handlers.js:remove-property`/`add-property`。测试 `tests/core/StateSelect.setPropExcluded.test.ts`、`StateSelect.scanProps.test.ts`、`W6.inspectorExcludeUI.test.ts`、`PropertyControlService.test.ts`、`Capability.propertyControl.test.ts`。
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## inspector 状态标记(徽标着色)
|
|
90
|
+
|
|
91
|
+
在原生属性检查器每行左侧贴状态机身份徽标,一眼看出受控/排除/脏值。**自动渲染,无需点击**(选中单个挂 StateSelectV2 的节点即生效):
|
|
92
|
+
|
|
93
|
+
- 灰方块带斜杠 = 已排除;粉三角 = 掉出控制(loose);黄半方块 = 部分子项未受控(mixed);蓝菱 = 状态机驱动;蓝菱套黄菱 = 当前 state 覆盖 default;黄左边框 = 录制脏值。
|
|
94
|
+
- 鼠标悬停徽标弹**多状态值表**(自定义 div 浮层,非原生 title——原生 title 慢)。
|
|
95
|
+
- 多选(>1 节点)或总开关关时清掉所有标记;全 tracked 的行不标。心跳 1.5s 重发现,dock 重建后自动重连。
|
|
96
|
+
- 总开关 + 子开关(可视化/脏值/排除徽标)在面板编辑视图 `#editor-actions`,走 `sendToMain`(main.js,Editor.Profile 持久化),**非** `_callScene`。
|
|
97
|
+
|
|
98
|
+
**真源**:`inspector-inject.js:updateRowStatus`/`apply`/`compIndexOfRow`/`buildTipHTML`、`scene-accessor.js:inspector-prop-status`(`rowKind` 分类)/`inspector-prop-state-values`、`main.js:inspector-mark-on`/`inspector-set-flags`;`StateSelectV2.ts:currentStateProps`。测试 `tests/core/StateSelect.inspector.test.ts`、`StateController.inspector.test.ts`(DOM 注入须 e2e)。
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 多控制器联动
|
|
103
|
+
|
|
104
|
+
声明"控制器 A 切到状态 X → 控制器 B 自动切到状态 Y",可序列化进 .fire/.prefab。
|
|
105
|
+
|
|
106
|
+
- **操作**:面板联动视图表单四下拉 + 「+ 添加联动」;关系图「✕」删边。inspector 不直显联动(行为在组件,UI 在面板)。
|
|
107
|
+
- **机制**:用 `targetCtrlId`(数字) 代替对象引用以便序列化,存进 `_bindingsData`(visible:false);运行时 `start()` 按 id 解析(onLoad 全登记后,不受加载顺序影响)。
|
|
108
|
+
- **不变量**:同 (sourceStateId,targetCtrl) 重复 add 覆盖;A→B→A 循环防护(单帧 `dispatching` 标志);目标 ctrlId 未加载时跳过不抛。
|
|
109
|
+
|
|
110
|
+
**真源**:`StateControllerV2.ts:addBinding`/`removeBinding`/`rehydrateBindings`、`MultiCtrlBindingCapability.ts:addBinding`/listener、`scene-accessor.js:add-binding`/`remove-binding`、`logic.js:_addBindingFromForm`/`_removeBinding`。测试 `tests/core/StateController.bindings.test.ts`/`multiCtrl.test.ts`、`Capability.multiCtrlBinding.test.ts`、`StateSelect.r3_multiCtrl.test.ts`。
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 状态切换(稳定 stateId vs 临时 index)
|
|
115
|
+
|
|
116
|
+
- **UI**:改控制器的 state 下拉(inspector `selectedIndex` / 面板 `_goState`,有 stateId 走 `set-state-by-id`,否则 `set-selected-index`)。
|
|
117
|
+
- **业务/panel 代码**:用稳定 API `SelectedPageIdCapability.setStateById(ctrl, stateId)` 切、`getSelectedStateId(ctrl)` 读。`selectedIndex` getter/setter 已 `@deprecated`,业务层禁止直接读写 index(reorder/delete 后会漂移);reorder 后 `getSelectedStateId` 跟着原 state 走。
|
|
118
|
+
|
|
119
|
+
**真源**:`SelectedPageIdCapability.ts:setStateById`/`getSelectedStateId`/`listAllStates`、`StateControllerV2.ts:selectedIndex` setter。测试 `tests/core/Capability.selectedPageId.test.ts`。
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 值搬运(StateSelectV2 单节点跨 state)
|
|
124
|
+
|
|
125
|
+
inspector StateSelectV2「值搬运」折叠组:「⇄ 与下一 state 交换值」(`swapValueWithNext`) / 「⎘ 复制值到下一 state」(`copyValueToNext`,深拷)。越界/最后一个 state 触发 next 为安全 noop,from===to 为 noop。
|
|
126
|
+
|
|
127
|
+
**真源**:`StateSelectV2.ts:swapValueWithNext`/`copyValueToNext`/`swapStateValues`/`copyStateValues`。测试 `tests/core/StateSelect.localStateValueOps.test.ts`。
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# StateController V1 → V2 迁移
|
|
2
|
+
|
|
3
|
+
把一个目标范围(某 prefab / 某活动 / 某 bundle)里的 V1 `StateController` / `StateSelect` 迁到
|
|
4
|
+
`StateControllerV2` / `StateSelectV2`:**prefab 序列化数据**走 `csc migrate`(确定性引擎),**业务脚本**走类型替换 + 切换 API 升级。
|
|
5
|
+
|
|
6
|
+
严格按下面顺序执行,每步都先复核再进下一步。机械改写交 `csc migrate`,判断与兜边交你(AI)。
|
|
7
|
+
|
|
8
|
+
## 关键不变量(先读)
|
|
9
|
+
|
|
10
|
+
- **V1 cid uuid**:controller `16b3e1ab-f9ea-4f09-ac6f-a92d6e80fdee`、select `0f62298b-7153-4520-a6bb-a5219b1b8f86`。
|
|
11
|
+
- **`.meta` 不可动**:prefab/脚本的 `.meta` 承载 Cocos uuid,迁移只改 `.prefab` / `.ts` 内容,绝不重生成 `.meta`(否则引用全断)。
|
|
12
|
+
- **stateId ≠ index**:V2 用稳定 `stateId` 切换,`selectedIndex` 是 index 级低层 API、随 reorder/delete 漂移、已 `@deprecated`。业务切换一律走 `SelectedPageIdCapability`。
|
|
13
|
+
- **`selectedIndex` 不能删**:`setStateById` 内部就是 `ctrl.selectedIndex = idx`,inspector 下拉、录制、panel 都依赖它。只标废弃、不删除。
|
|
14
|
+
- **V1 runtime 保留**:迁移单个范围不删 V1 源码,项目其它地方可能仍在用。全量迁完才删。
|
|
15
|
+
|
|
16
|
+
## 🔴 前置安全门:local vs remote bundle
|
|
17
|
+
|
|
18
|
+
迁移会把 prefab 里的 V1 cid 改成 V2 cid。**老客户端只有 V1 runtime,拉到 V2-cid 的 prefab 会反序列化崩溃。**
|
|
19
|
+
|
|
20
|
+
- prefab 在 **`assets/RemoteBundles/...`**(或 bundle meta 标 remote)= 热更下发 → **危险**。`csc migrate` **默认拒绝**这类 prefab;必须确认「全量客户端已发版铺到 V1+V2 共存 runtime、老客户端跌破阈值」后,才用 `--allow-remote` 放行并热更。
|
|
21
|
+
- prefab 在随包发版的 **local bundle** = 脚本与 prefab 同包发布 → 相对安全(仍走正常发版)。
|
|
22
|
+
|
|
23
|
+
## 迁移流程
|
|
24
|
+
|
|
25
|
+
### 1. 发现范围
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# 含 V1 控制器的 prefab(cid 残留 / stateValue / V1 类名)
|
|
29
|
+
grep -rlE '16b3e1ab-f9ea-4f09-ac6f-a92d6e80fdee|0f62298b-7153-4520-a6bb-a5219b1b8f86|"stateValue"|"StateController"|"StateSelect"' <目标范围> --include='*.prefab' --include='*.fire'
|
|
30
|
+
# 引用 V1 的业务脚本
|
|
31
|
+
grep -rnE 'Controller/StateController|Controller/StateEnum|@property\(StateController\)|getComponent\(StateController\)|: StateController' <目标范围> --include='*.ts'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. prefab 迁移(dry-run → write → 复核)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# a. dry-run,人工看统计是否合理(controllers / selects / stateValues / propKeysMigrated)
|
|
38
|
+
csc migrate <prefab>
|
|
39
|
+
|
|
40
|
+
# b. 写入(git 仓库用 git 当备份;非 git 加 --backup)
|
|
41
|
+
csc migrate --write <prefab>
|
|
42
|
+
|
|
43
|
+
# c. 再 dry-run,预期 changedFiles=0(幂等)
|
|
44
|
+
csc migrate <prefab>
|
|
45
|
+
|
|
46
|
+
# d. 残留复核,全部应为 0
|
|
47
|
+
grep -cE '"stateValue"|"StateController"|"StateSelect"|16b3e1ab|0f62298b' <prefab>
|
|
48
|
+
```
|
|
49
|
+
> remote bundle 的 prefab 会被 `csc migrate` 拒绝;确认发布时序后加 `--allow-remote`。
|
|
50
|
+
|
|
51
|
+
### 3. stateId == index 体检(决定切换 API 能否 drop-in)
|
|
52
|
+
|
|
53
|
+
V2 切换用 stateId。若各 controller 的 `stateId` 恰好等于其 index(无历史删除时通常如此),且业务枚举值也按 0,1,2… 设计,则 `selectedIndex = enum` → `setStateById(ctrl, enum)` 是**零风险 drop-in**;否则需把业务枚举重映射成对应 stateId。
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python3 - <prefab> <<'PY'
|
|
57
|
+
import json,sys
|
|
58
|
+
d=json.load(open(sys.argv[1]))
|
|
59
|
+
for i,o in enumerate(d):
|
|
60
|
+
if isinstance(o.get("_states"),list):
|
|
61
|
+
sid=[d[r["__id__"]].get("stateId") for r in o["_states"] if isinstance(r,dict) and "__id__" in r]
|
|
62
|
+
if sid: print(f"ctrl@{i} stateIds={sid} index==stateId? {sid==list(range(len(sid)))}")
|
|
63
|
+
PY
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 4. 业务脚本:类型替换
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
import { StateController } from ".../Controller/StateController" → StateControllerV2 from ".../ControllerV2/StateControllerV2"
|
|
70
|
+
import { EnumStateName } from ".../Controller/StateEnum" → from ".../ControllerV2/StateEnumV2"
|
|
71
|
+
@property(StateController) / getComponent(StateController) / : StateController → ...V2
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 5. 业务脚本:切换 API 升级到 SelectedPageIdCapability
|
|
75
|
+
|
|
76
|
+
加导入:
|
|
77
|
+
```ts
|
|
78
|
+
import { SelectedPageIdCapability } from ".../ControllerV2/capabilities/SelectedPageIdCapability";
|
|
79
|
+
```
|
|
80
|
+
替换(**保留原 null 守卫**;先转「写」再转「读」,避免把 `X.selectedIndex = Y` 误转成 `getSelectedStateId(X) = Y`):
|
|
81
|
+
```ts
|
|
82
|
+
ctrl.selectedIndex = X; // 写 → SelectedPageIdCapability.setStateById(ctrl, X);
|
|
83
|
+
const s = ctrl.selectedIndex; // 读 → const s = SelectedPageIdCapability.getSelectedStateId(ctrl);
|
|
84
|
+
```
|
|
85
|
+
**易错点**:
|
|
86
|
+
- 只动 **StateController 类型的引用**。别碰其它组件的 `.selectedIndex`(如 `PageView` / 自定义 SliderTabbar)。
|
|
87
|
+
- 注释里的 `selectedIndex` 顺手更新成 capability 说法,但别让注释参与正则误伤代码。
|
|
88
|
+
- `EnumStateName` 若升级后不再被引用,删掉其 import 免 lint 报错。
|
|
89
|
+
|
|
90
|
+
### 6. 复核
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
grep -nE '\.selectedIndex' <目标范围> --include='*.ts' # 业务代码无 .selectedIndex 残留(排除注释与非 controller 组件)
|
|
94
|
+
grep -nE 'SelectedPageIdCapability' <目标范围> --include='*.ts' # capability 调用与导入到位
|
|
95
|
+
( cd tests && npx jest ) # 跑 V2 测试套件(隔离环境)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 编辑器人工验证(脚本/测试覆盖不到,必须人来做)
|
|
99
|
+
|
|
100
|
+
1. 打开迁移后的 prefab:**无 missing component / 脚本报错**;V2 controller/select 正常显示状态下拉。
|
|
101
|
+
2. **`@property` / `getComponent` 连线未断**:业务组件对 controller 的引用仍指向迁移后的 V2 组件。
|
|
102
|
+
3. 预览跑一遍:状态切换的视觉与迁移前一致。
|
|
103
|
+
|
|
104
|
+
## 输出要求
|
|
105
|
+
|
|
106
|
+
回答用户时说明:改了哪些 prefab/脚本、是否 dry-run/write、迁移统计、残留是否为 0、stateId==index 体检结果、跑了哪些测试、**remote bundle 的发布时序提醒**、以及需用户在编辑器确认的 3 点。
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# 上行回流 PR
|
|
2
|
+
|
|
3
|
+
把 consumer 工程里对 managed 文件(runtime + panel)的本地改动,回流到源仓
|
|
4
|
+
`github.com/fenglimg/cocos-state-controller`。CLI 算差异(确定性),你(AI)做判断与编排(看懂改动、按政策取舍、写提交、开 PR)。
|
|
5
|
+
|
|
6
|
+
## 前置
|
|
7
|
+
- consumer 工程已 `csc install`(存在 `.csc/lock.json`)。
|
|
8
|
+
- 已装 `gh` CLI 且登录(`gh auth status`)。
|
|
9
|
+
|
|
10
|
+
## 流程
|
|
11
|
+
|
|
12
|
+
### 1. 算上行差异(CLI 确定性步骤)
|
|
13
|
+
```bash
|
|
14
|
+
csc sync --upstream --output /tmp/upstream.patch
|
|
15
|
+
```
|
|
16
|
+
- 输出是 canonical 路径(源仓布局)标签的 unified diff,含 modified / added / removed。
|
|
17
|
+
- 范围只 managed runtime+panel,不含 tests(CLI 已保证)。
|
|
18
|
+
- 无改动则结束,无需 PR。
|
|
19
|
+
|
|
20
|
+
### 2. 按漂移政策取舍(本流程的判断核心)
|
|
21
|
+
逐个 hunk 审查,**只回流符合单一规范形态的改动**:
|
|
22
|
+
- ✅ **真实 bug fix / 功能改进** → 收入 PR。
|
|
23
|
+
- ✅ **两端通吃的 env 收敛写法**(如统一 `Array.from(new Set())`)→ 收入。
|
|
24
|
+
- ❌ **per-consumer 本地 env 适配**(只为本工程打补丁)→ **剔除**,这是源仓 bug,应在源仓两端通吃地修,而非把单边适配上行。
|
|
25
|
+
- ❌ **路径/安装位置差异** → 剔除(CLI 反归一化应已消除;若残留说明归一化漏了,记 issue)。
|
|
26
|
+
- ❌ **平台分支硬 fork 文件** → 剔除,改用 in-file `CC_EDITOR` 守卫的写法再上行。
|
|
27
|
+
|
|
28
|
+
把保留的 hunk 重组成干净 patch(可拆多个逻辑提交)。
|
|
29
|
+
|
|
30
|
+
### 3. 在源仓开 PR
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/fenglimg/cocos-state-controller /tmp/scv2-src # 若未克隆
|
|
33
|
+
cd /tmp/scv2-src
|
|
34
|
+
git checkout -b upstream/<简短主题>
|
|
35
|
+
git apply /tmp/upstream.patch # 或仅 apply 取舍后保留的 hunk
|
|
36
|
+
```
|
|
37
|
+
- patch 路径已是源仓 canonical 布局,直接 `git apply` 落位。
|
|
38
|
+
- 若源仓 main 已领先,按标准 PR 流程:git 三方合并 / rebase 后再提。
|
|
39
|
+
|
|
40
|
+
### 4. 写提交 + 开 PR
|
|
41
|
+
- 提交信息用中文,格式 `类型: 简短描述`(feat/fix/refactor…)。
|
|
42
|
+
- 每个逻辑改动一个提交,说清「为什么这是两端通吃的改动,而非单边适配」。
|
|
43
|
+
```bash
|
|
44
|
+
git commit -am "fix(controller): <说清根因与通吃理由>"
|
|
45
|
+
git push -u origin upstream/<主题>
|
|
46
|
+
gh pr create --repo fenglimg/cocos-state-controller \
|
|
47
|
+
--title "<类型: 描述>" \
|
|
48
|
+
--body "$(cat <<'EOF'
|
|
49
|
+
## 来源
|
|
50
|
+
consumer 工程上行回流(csc sync --upstream)。
|
|
51
|
+
|
|
52
|
+
## 改动取舍(漂移政策)
|
|
53
|
+
- 收入:<列保留的改动 + 通吃理由>
|
|
54
|
+
- 剔除:<列剔除的本地适配 + 原因>
|
|
55
|
+
|
|
56
|
+
## 验证
|
|
57
|
+
源仓 CI(release workflow)跑全测试门禁。
|
|
58
|
+
EOF
|
|
59
|
+
)"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 不变量
|
|
63
|
+
- **绝不上行 per-consumer 单边适配**;要适配就在源仓两端通吃地改。
|
|
64
|
+
- **绝不动 canonical `.meta` uuid**(包独占固定)。
|
|
65
|
+
- patch 范围只 managed 文件;consumer 业务代码不回流。
|
|
66
|
+
- 拿不准某 hunk 是否「通吃」时,停下问人,不擅自上行。
|