Files
Cielonos/design/quick-specs/coordinator-runtime-policy-2026-07-04.md
SoulliesOfficial 39b43680a9 爆更
2026-07-18 03:16:20 -04:00

250 lines
9.6 KiB
Markdown
Raw 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.
# Quick Design Spec: Coordinator Runtime Policy
**Type**: Reference / Runtime Policy
**System**: Combat Coordinator / Minion Encounter AI
**Related Specs**:
- `design/quick-specs/minion-formation-showcase-combat-2026-07-03.md`
- `design/quick-specs/minion-combat-feel-and-weapon-roles-2026-07-03.md`
**Date**: 2026-07-04
## Change Summary
本文整理 `CoordinatorSubmodule` 改造期间形成的设计约定,用于指导后续杂兵 AI、Behavior Designer 节点、波次配置和命中反馈系统的开发。
核心目标不是让杂兵完全服务玩家,也不是让杂兵无约束地追求最优围攻,而是让 Cielonos 的杂兵战形成一种“有战术倾向,但仍然镜头友好、节奏清晰、打击稳定”的中高速 ARPG 体验。
## Current Implementation State
当前已经完成:
- `FormationProfile`:使用可配置 `Sector` 描述杂兵站位区域。
- `FormationSectorPurpose`:区分 `Action``FreeArea`
- `FormationTacticalRoleMask`:允许 Sector 声明可接受的战术职责。
- `CoordinatorSubmodule` profile-driven sector assignment旧固定 6 扇区逻辑已替换为 profile-driven runtime slots。
- Stable random target敌人取得 Sector 时,会在 Sector 内抽取一个稳定随机点,避免整齐拼阵。
- `FormationFrame``PlayerForward` / `CameraForward` 只作为阵型初始化方向,不再逐帧拖动已分配 Sector。
- Path crossing penalty候选 Sector 如果导致敌人从玩家附近横穿,会被显著降权。
- `RefreshFormationFrame()`:已提供手动刷新阵型方向的方法,但尚未接入自动 policy。
当前仍未完成:
- 敌人 role assignment 仍主要停留在接口和数据层Behavior Designer 尚未真正按 `FormationTacticalRole` 分流行为。
- `RefreshFormationFrame()` 尚未由波次、房间或 Coordinator policy 自动调用。
- 玩家命中敌人后的 `Movement Suppression` 尚未实现。
- `FreeArea` 敌人还缺少低威胁行为策略,例如展示位、待机位、挑衅、缓慢重定位。
- `AttackToken``FormationTacticalRole` 还没有完全合并为统一威胁预算。
## Design Principles
### 1. Formation is Tactical Staging, Not Real-Time Rotation
`FormationProfile.referenceMode` 的职责是决定阵型初始化方向,而不是让阵型每帧跟随玩家或摄像机旋转。
正确语义:
```text
referenceMode = FormationFrame 初始化方向来源
FormationFrame.rotation = 当前战斗阶段的稳定阵型方向
Player position = 当前阵型中心,可持续跟随玩家
```
这样可以保留 `CameraForward` / `PlayerForward` 的战术意义,同时避免玩家转身或摄像机旋转导致敌人频繁横向移动。
### 2. RefreshFormationFrame is a Combat Director Event
`RefreshFormationFrame()` 不应在普通 AI Tick 中调用。它表示“当前战斗阶段需要重新定义舞台方向”,因此应由房间、波次、阶段切换或 Coordinator policy 触发。
推荐调用时机:
| Trigger | Rule |
|---------|------|
| 新一波敌人生成 | 刷新一次,使新波次以当前镜头或玩家朝向组织阵型 |
| CombatRoom / Zone 开始 | 默认由第一次 `TryAcquireSector` 创建 `FormationFrame`,通常不需要额外调用 |
| 所有 `Action` Sector 空闲且敌人未处于攻击/受击/连段中 | 可作为重整期刷新 |
| 玩家发生大位移 | 玩家离开原战场约 8-12 米后,可刷新一次 |
| 战斗阶段切换 | 例如从玩家输出窗口切换到敌人反击窗口 |
| 特殊演出或脚本事件 | 可显式刷新,但应避免打断当前受击或攻击行为 |
不推荐调用时机:
- 玩家只是转身。
- 摄像机只是旋转。
- 敌人正在 `MoveToSector`
- 玩家正在近战连段命中敌人。
- 敌人正在攻击前摇、攻击中、受击、击飞、倒地或硬直。
- Boss 战中杂兵只是辅助压力时。
推荐约束:
```text
minRefreshInterval = 3.0s - 5.0s
largePlayerDisplacement = 8m - 12m
onlyRefreshWhenNoHighPriorityState = true
```
### 3. Hit Ownership Overrides Formation
当玩家近战命中普通杂兵时,敌人应短时间进入玩家拥有的表现窗口。这个窗口内,敌人的主动移动和阵型重定位应被抑制。
建议机制名:
```text
Movement Suppression
Player-Owned Window
```
允许保留:
- 受击动画。
- 击退、浮空、牵引、倒地。
- `ReactionSubcontroller` 被动位移。
- `FuncAnimPayload``AttackArea` 施加的位移。
- Hit stop、camera shake、damage number、Wwise hit event。
应暂停:
- `MoveToSector`
- 主动重新申请 Sector。
- 主动刷新 Sector 目标点。
- 主动攻击请求。
- 绕背、归位、重新排队。
建议初始数值:
| Enemy Type | Movement Suppression |
|------------|----------------------|
| 普通近战杂兵 | 0.6s - 1.0s |
| `ShowcaseVictim` | 0.9s - 1.4s |
| 远程/轻型敌人 | 0.4s - 0.7s |
| 精英敌人 | 0.2s - 0.5s |
| Boss / `C1_Axe` | 不走此普通规则,使用 Boss poise 机制 |
### 4. Threat Budget Must Be Explicit
杂兵战应有战术倾向,但不能让所有敌人同时成为高威胁单位。
Demo 阶段推荐:
```text
maxActiveMeleeThreats = 1
maxActiveRangedThreats = 0-1
maxTotalActiveThreats = 2
defaultNonThreatRole = ShowcaseVictim / Reposition / Taunt
```
`AttackToken` 目前已经承担部分威胁预算功能,但还需要与 `FormationTacticalRole` 结合。未来应避免“没有攻击 token 的敌人仍继续强行攻击玩家”。
### 5. FreeArea is Still Formation
`FreeArea` 不是无规则闲逛区域,而是低威胁敌人的舞台调度区域。
推荐用途:
- `ShowcaseVictim`:靠近玩家前场,等待被连段或压制。
- `Reposition`:填补镜头友好站位。
- `Taunt`:做低威胁展示动作或短距离摆位。
- `Staging`:等待下一次 role 切换。
`FreeArea` 可以比 `Action` 更活跃,但刷新频率仍应较低。例如每 3-6 秒、玩家大位移后、或敌人脱离镜头太久时重取目标点。
## Runtime Priority Stack
杂兵 AI 的优先级建议如下:
```text
Death / Disabled
> Hit Reaction / Player-Owned Window
> Current Attack / Telegraph / Recovery
> AttackToken / Threat Budget
> Formation Sector Assignment
> FreeArea Showcase / Reposition
> Idle / Taunt
```
这意味着 Formation 不是最高优先级。它负责组织战场,但不能打断受击表现、攻击前摇、击飞、倒地或玩家连段窗口。
## Behavior Designer Integration Notes
现有节点:
- `TryAcquireSector`
- `MoveToSector`
- `ReleaseSector`
- `TryAcquireAttackToken`
- `HasAttackToken`
- `ReleaseAttackToken`
建议新增或扩展:
| Node | Purpose |
|------|---------|
| `TryAcquireSectorByRole` | 按 `FormationTacticalRole` / `FormationSectorPurpose` 申请 Sector |
| `HasAssignedRole` | 行为树根据 role 分流 |
| `IsMovementSuppressed` | 被玩家命中后阻止主动移动 |
| `CanRequestAttackToken` | 集中检查 threat budget、受击状态、展示窗口 |
| `RequestFormationRefresh` | 仅由波次或特殊行为触发,不放入普通移动循环 |
| `MoveToFormationSector` | 未来替代或扩展 `MoveToSector`,支持到达后停留、低频重取点 |
现阶段可继续兼容旧节点,但下一阶段应优先让 L1/L2 行为树显式读取 role。
## Suggested Next Implementation Order
### Step 1. Movement Suppression
实现玩家近战命中后的主动移动抑制。
最低要求:
- 在敌人被玩家近战命中时记录 `movementSuppressedUntil`
- `MoveToSector` 在抑制期间返回 `Running` 或进入等待,不刷新 destination。
- 攻击 token 请求在抑制期间失败。
- 受击、击退、浮空等被动位移仍然保留。
### Step 2. Role-Aware Behavior Tree
让杂兵行为树不再只问“有没有 Sector”而是问“我现在是什么职责”。
优先对象:
- `L1_Assault_Pawn`
- `L2_Assault_Stinger`
### Step 3. RefreshFormationFrame Policy
接入自动刷新策略,但先保守实现。
最低要求:
- 房间/波次开始时允许刷新。
- 玩家大位移后允许刷新。
- 有 cooldown。
- 有敌人处于受击、攻击、硬直时不刷新。
### Step 4. FreeArea Low-Threat Behaviors
`ShowcaseVictim` / `Reposition` / `Taunt` 提供简单行为。
不要求新动画和 VFX先通过移动、停留、朝向玩家、等待、轻微重定位完成。
### Step 5. Threat Budget Unification
`AttackToken``FormationTacticalRole`、enemy rank、当前战斗阶段统一为一套 Coordinator policy。
## Acceptance Criteria
- [ ] 玩家转身或旋转摄像机时,已分配 Sector 的敌人不会频繁横向重定位。
- [ ] 玩家近战命中普通杂兵后,敌人不会立刻为了 Formation 逃出玩家攻击范围。
- [ ] 敌人从一个 Sector 移动到另一个 Sector 时,优先避免从玩家身体附近横穿。
- [ ] `RefreshFormationFrame()` 只在战斗阶段断点、波次变化或大位移重整时调用。
- [ ] `Action` 敌人与 `FreeArea` 敌人在行为意图上可区分。
- [ ] Demo 阶段同一时间的高威胁杂兵数量可控。
- [ ] `C1_Axe` Boss 战不被普通杂兵 Coordinator 规则误削弱。
## Open Questions
- `Movement Suppression` 应放在 `StatusSubmodule``ReactionSubcontroller`、还是专门的 AI state 中?
- 玩家攻击类型如何区分 melee hit、projectile hit、support equipment hit
- `RefreshFormationFrame()` 应由 `CombatRoomSubmodule``RunManager`、还是 Coordinator 自身 policy 触发?
- `FreeArea` 敌人是否需要单独的 low-threat token避免所有自由人同时轻微移动造成视觉噪声
- 高难度 profile 中,`Flanker` 是否可以绕背攻击,还是只允许绕背施压但不攻击?