Initial project sync

This commit is contained in:
wxm
2026-07-06 22:55:14 -07:00
commit 8cca00d0da
1066 changed files with 58585 additions and 0 deletions
+1721
View File
File diff suppressed because it is too large Load Diff
+275
View File
@@ -0,0 +1,275 @@
# 第一关关卡设计(Level 1 Design
> 本文档承接《Anchor策划书1.0》(docs/AnchorV1.0.md,缩写 §)与《关卡设计指南》。
> §7 / §8 两节为指南原文;其余章节是落地到本项目的关卡与行为设计说明。
> 实现入口:`scenes/stage/level_director.gd`(出怪导演)、`scenes/enemies/minion_behavior.gd`(小怪状态机)、`scenes/stage/boss_room_gate.gd`Boss 房门)。
---
## 1. 关卡布局
沿用 `rthythm_archor_game_player` 参考工程的视觉基线:`Stage` 使用跟随相机,`Camera2D` 初始位置为 `(2047, 395)`,缩放为 `(1.25, 1.25)`;玩家、怪物、Boss 的地面锚点统一在 `y = 560`。关卡迁移只调整出怪方式、门线和战斗流程,不改变角色、Boss、怪物、背景、HUD 的相对大小与原有镜头比例。
| 位置 | x 坐标 | 说明 |
|---|---:|---|
| 竞技场左边界 | 1120 | 巡逻 / 后撤永不越界 |
| 左侧刷新点 | 1420 | 增援入场起点,使用 `LevelDirector.left_edge_x` |
| 玩家出生点 | 1180 | 2026-07-05 new2:改为地图偏左,开场向右推进约 1000px 遇敌 |
| 战斗区域中心 | 2047 | 增援入场目标的基准 |
| 阶段一近战怪预置点 | 2235 | 编辑器摆放,无刷新动画 |
| Boss 初始锚点 | 2520 | 保留参考工程站位;玩家未进 Boss 战前 `combat_enabled = false` |
| 右侧刷新点 | 2670 | 增援入场起点,使用 `LevelDirector.right_edge_x` |
| 竞技场右边界 | 2890 | 巡逻 / 后撤永不越界 |
| Boss 房门 / 触发线 | 2960 | 三态:封锁→开放→反锁;作为通关后进入 Boss 战的流程线 |
Boss 房门三态(`BossRoomGate`):
- **封锁(sealed,蓝灰色)**:开局即封,物理墙阻挡,直到 6 只小怪全灭。
- **开放(绿色)**`LevelDirector` 调用 `unseal()`,墙体关闭,可通行。
- **反锁(红色)**:玩家跨过门线后在身后锁死(§4.5,不可回头刷怪),并触发 Boss 战入场规则;不移动 player 项目原有相机,不改变 HUD / 角色 / Boss 的比例。
## 2. 小怪行为逻辑(MinionBehavior 状态机)
决策与攻击全部对齐节拍(`beat_ticked`),移动逐帧执行。攻击动作进行中不移动、不换目标。
### 2.1 近战怪(role = melee
```text
入场(Entry) → 巡逻(Patrol) ⇄ 追击(Chase) → [进入攻击距离后按拍攻击]
```
- **入场**:从画面边缘走到预定战斗区域(`entry_target_x`);途中玩家进入警戒范围则提前转入追击。
- **巡逻**:以锚点 ±110px 低速(0.45×)往返,面朝行走方向;阶段一预置怪以出生点为锚点。
- **警戒→追击**:玩家进入警戒范围(420px)后,**在下一个节拍点**切入追击(指南 §7.4);追击时全速逼近,停在 110px 处。
- **脱战**:玩家甩开警戒范围 1.25 倍距离 → 回到当前位置小范围巡逻。
- **攻击**:距离 ≤150px 时按拍出手;强势每 1 拍一次,弱势每 2 拍一次(§17.2)。
### 2.2 远程怪(role = ranged
```text
入场(Entry) → 站位固定(Hold) → [距离判断按拍射击];受压时 后撤(Retreat) → 新站位(Hold)
```
- **不巡逻**。入场只做一段移动:走到预设站位后固定(指南 §7.4)。
- **距离判断**:玩家在射程内(430px)按拍射击;离开警戒范围(540px)即停止行动。
- **后撤(调整站位)**:满足任一条件且 3 秒冷却就绪时,向远离玩家方向快速(1.7×)退开 190px:
- 玩家近身(≤150px)持续 1.1 秒;
- 1.5 秒内连续受击 2 次且玩家仍在近旁。
- 贴墙无退路时改为穿过玩家身位向另一侧跳开。
### 2.3 行为参数表
| 参数 | 近战 | 远程 |
|---|---:|---:|
| 警戒范围 | 360 | 540 |
| 攻击距离 | 120 | 450(≤ 镜头半宽 460,不许屏幕外开火) |
| 停步距离 | 120 | 340 |
| 攻击判定范围 | 前方 ≈140 | — |
| 巡逻半径 / 速度 | 110 / 0.45× | — |
| 后撤距离 / 速度 / 冷却 | — | 220 / 1.7× / 3s |
| 受击脱离 | — | 连续受击 4 次(2s 链窗)→ 闪烁无敌 0.4s + 跳跃脱离(3.2×) |
> 2026-07-05 new3 定案更新:近战小怪基础攻击 50(远程 25 的两倍);远程怪的
> 近身后撤触发距离 220pxBoss 见 §4 及 decisions.md 当日条目(无韧性系统,
> 普攻恒霸体、耗能技能才可压制)。
## 3. 强弱状态与打断规则(核心规则)
**只有处于强势状态的怪,才能打断玩家当前的状态。**
- 打断 = 玩家受击进入 Hitstun、当前动作被取消、被击退。三者同生同灭。
- 弱势状态怪的攻击**只结算伤害**:不打 Hitstun、不取消玩家动作、不产生击退。玩家可以顶着弱势怪的攻击继续连段——这正是“在怪物弱势相位输出”的教学奖励。
- 强弱在**动作开始时快照**(§17.4):出手瞬间是弱势,即使命中前相位切换,本次攻击依然没有打断权;反之亦然。
- Boss 不分强弱(§18.1),**恒定视为强势**,攻击永远可以打断玩家。
- 实现:`DamageEmitter.attacker_interrupts`(近战,configure_hit 时快照)与弹丸 `attacker_interrupts`(发射时快照,经 `projectile_requested` 上下文传递)→ `CombatResolver` 并入 `interrupts` 并把弱势击退清零 → `HealthComponent` / `CombatManager` 原有打断管线不变。
### 3.1 每跳伤害预算(玩家 HP 300)
弹丸伤害基数改为**发射者自己的基础攻击力**(玩家 100 / 小怪 25 / Boss 30),并结算发射者的强弱倍率——修复前弱势远程怪一发 45 反而比强势近战怪(18)疼。
| 敌人 × 相位 | 状态 | 单次伤害 | 攻击间隔 | 可打断玩家 |
|---|---|---:|---|---|
| 近战怪 × Past | 强势 | ≈1825×0.5×1.4 | 每 1 拍 | ✔ |
| 近战怪 × Future | 弱势 | ≈1025×0.55×0.7 | 每 2 拍 | ✘ |
| 远程怪 × Past | 弱势 | ≈825×0.45×0.7 | 每 2 拍 | ✘ |
| 远程怪 × Future | 强势 | ≈2325×0.65×1.4 | 每 1 拍 | ✔ |
| Boss × Past | — | 24~30+30×0.8+ | 决策每 2 拍 | ✔ |
| Boss × Future | — | ≈1830×0.6 | 决策每 1 拍 | ✔ |
承伤端沿用 §17.2/§17.3:强势承伤 25%,弱势承伤 250%(弱势怪 ≈1 个重技能+补刀击杀)。
## 4. Boss 行为逻辑
行为树(`boss_behavior_tree.gd`)按当前相位限定招式池并调整节奏(§18.1):
- **Past(慢而重)**:只用近战——三段地面连击轮转、远距冲刺逼近、中距突刺;决策每 2 拍一次。
- **Future(快而轻)**:只用远程——两种弹幕轮转、贴脸时 2 向弹幕逼退;决策每 1 拍一次。
- 特殊攻击(突刺 / 2 向弹幕)前 2 拍生成条件型 TimeAnchorEvent(§18.2):玩家失锚则相位翻转、Boss 状态改变。
- 被连续命中 4 次触发后撤位移(读条逃脱),防止无限压制;HP / 承伤不随相位变化。
- 玩家未进 Boss 房时 `combat_enabled = false`:不出手、不产生时间锚点。
## 5. 挑战性与可通关性
- **教学曲线**:阶段一单怪教基础节奏与相位强弱 → 阶段二单远程怪教射程与站位 → 阶段三混编教目标优先级(“打当前弱势的那只”)。
- **压力上限受控**:增援分 4 波、左右交替、每 2 秒 1 只(约 8 秒刷完),不会瞬间包围;同一时刻最多 1~2 只强势怪能打断玩家,其余只能磨血。
- **拖延惩罚**:拖战会让场上怪数上升(指南 §7.5),但总量封顶 6 只。
- **保底通关**:全弱势输出窗口每次相位切换必然出现;弱势怪不打断意味着玩家连段不会被杂兵摸一下就断,Miss 惩罚主要来自强势怪与自己的节奏失误。
- **数值下限**:最坏情况(Past 相位 2 近战强势贴脸)≈36 伤/拍,玩家仍有 ≥8 拍反应窗口;Future 相位 2 远程强势 ≈46 伤/拍但可走位躲弹、近身逼后撤。
---
## 7. 第一关小怪出怪流程
第一关共出现 6 只小怪,分为 3 个出怪阶段。
---
### 7.1 阶段一:初始近战怪
出怪内容:
- 近战怪 × 1
生成规则:
- 玩家进入第一关时,第一只近战怪已经存在于预定位置
- 该怪物不需要刷新动画
- 玩家靠近后,按照近战怪行为逻辑进入巡逻、警戒、追击或攻击状态
教学目标:
- 让玩家熟悉基础攻击节奏
- 让玩家理解近战怪的巡逻、追击和攻击逻辑
- 让玩家第一次观察相位切换对近战怪强弱状态的影响
教学重点:
- 过去相位下,近战怪是强势状态
- 未来相位下,近战怪是弱势状态
- 玩家应尝试在怪物弱势相位进行输出
---
### 7.2 阶段二:近距离刷新远程怪
触发条件:
- 玩家击败第一只近战怪
出怪内容:
- 远程怪 × 1
生成规则:
- 第一只远程怪在玩家附近不远处刷新
- 刷新位置应在玩家可感知范围内
- 不应直接贴脸刷新
- 刷新后远程怪进入静止或距离判断状态
教学目标:
- 让玩家熟悉远程怪的攻击范围
- 让玩家理解远程怪不巡逻,但会调整站位
- 让玩家观察远程怪在不同相位下的强弱变化
教学重点:
- 过去相位下,远程怪是弱势状态
- 未来相位下,远程怪是强势状态
- 玩家需要利用相位切换寻找输出窗口
---
### 7.3 阶段三:左右两侧增援刷新
触发条件:
- 玩家击败第二只怪物,也就是第一只远程怪
出怪内容:
- 近战怪 × 2
- 远程怪 × 2
生成规则:
- 四只怪物从玩家左右两侧刷新
- 刷新点位于摄像机范围外,或玩家不容易直接看到的位置
- 怪物刷新后,从画面边缘进入摄像机范围
- 刷新不是一次性全部出现,而是按间隔依次出现
- 推荐每 2 秒刷新 1 只
- 约 8 到 10 秒内完成全部 4 只怪物的刷新
推荐刷新顺序:
| 顺序 | 时间 | 怪物 | 方向 |
|---|---:|---|---|
| 1 | 0 秒 | 近战怪 | 左侧 |
| 2 | 2 秒 | 远程怪 | 右侧 |
| 3 | 4 秒 | 近战怪 | 右侧 |
| 4 | 6 秒 | 远程怪 | 左侧 |
> 具体方向可以根据关卡地形调整,但需要保证玩家不会被瞬间包围。
---
### 7.4 增援怪物入场逻辑
#### 近战怪入场
- 近战怪从左右边缘进入摄像机范围
- 进入后先移动到预定战斗区域
- 如果玩家进入警戒范围,立刻在下一个节拍点进入追击状态
- 如果玩家未进入警戒范围,则进入小范围巡逻状态
#### 远程怪入场
远程怪不巡逻,但需要有入场移动。
- 远程怪从画面边缘进入
- 移动到预设站位
- 到达站位后固定下来
- 固定后开始判断玩家是否进入攻击范围
- 如果玩家离开警戒范围,远程怪停止行动
- 如果玩家近身过久或连续受击,远程怪尝试后撤或跳开
---
### 7.5 阶段三教学目标
阶段三的目标是让玩家同时处理多种敌人压力。
教学目标:
- 让玩家同时面对近战压力和远程威胁
- 让玩家理解不同怪物在同一相位下强弱不同
- 训练玩家根据当前相位决定优先攻击目标
- 让玩家体验拖延战斗会导致敌人逐步增多
教学重点:
- 过去相位:近战怪强,远程怪弱
- 未来相位:近战怪弱,远程怪强
- 玩家需要根据当前相位选择更容易击杀的目标
- 优先击杀弱势相位敌人,可以降低战斗压力
---
## 8. 第一关通关条件
第一关普通战斗区域的通关条件为:
- 击败全部 6 只小怪
具体要求:
- 击败初始近战怪
- 击败刷新出的第一只远程怪
- 击败左右两侧增援刷新的 4 只怪物
全部小怪被击败后:
- 开放前往 Boss 房的路径
- 玩家可以进入 Boss 房
- 进入 Boss 房后触发 Boss 战
+696
View File
@@ -0,0 +1,696 @@
# Migration Decisions
## 2026-07-06 第八轮:视差语义定案(远景天幕,夕阳恒在画面内)
策划澄清视差意图:背景是**远景天幕**,夕阳要时刻在画面内——即背景应
大比例**跟随相机**(远景几乎不动的错觉),而非此前理解的"背景比世界慢
一点"。此前 0.92/0.8 的参数把背景几乎钉在世界上,方向整个反了,这才是
连续几轮"看不出视差"的真因。
1. **参数**`PARALLAX_SCROLL_SCALE := 0.2`(背景仅以 0.2 倍速随世界滚动,
80% 跟随相机);`PARALLAX_ANCHOR_X := 1734.5`(让夕阳——原画 x≈760、
世界 x≈1656——在出生点镜头可见中心 1340.8 恰好居中)。
2. **验算+截图验证**:相机可见中心全程 [1340.8, 2753.2],夕阳屏幕位置从
正中缓移到左 282px,始终在画面内(含光晕);两端背景覆盖充足
(极左窗⊂[581.5,2882.5]、极右窗⊂[1711.5,4012.5])。窗口化实测四点
截图确认:出生点夕阳居中、中段/封门处缓移仍在画面中央区域,未来
相位(红色天幕)同样成立;世界物件大幅滚动 vs 天幕缓动的对比即
视差观感本体。
3. **已知取舍加剧说明**:天幕语义下步道砖(烙在原画里)随镜头大幅跟随
(奔跑时脚下滑动 176px/s)——单张烙死图的固有代价;美术把"近景步道"
拆成独立 1:1 层后即可消除,其余层保持现参数。
4. 可视化验证配方(复用):manage_scene=false + 手动挂 main.tscn +
start_game()Title 态直接 _set_state 不行(树暂停 + 标题屏不透明底色)。
## 2026-07-06 第七轮修复(玩家与敌人重叠:冲刺穿人收尾排斥)
策划确认:冲刺穿人机制保留,其余时间玩家不得与小怪/Boss 重叠。
1. **调查结论**:身体碰撞矩阵完整且实测有效(玩家 layer32/mask65,敌人
layer64/mask33,平移互撞精确挡在半宽和 19px)。重叠的真实来源是
dash_through 的收尾策略:落点嵌在敌人体内时,恢复碰撞的逻辑"干等分离"
(07-04 定案,防止嵌入恢复被反推回入口侧)——嵌着就无限期保持幽灵态,
玩家可长期站在敌人身体里;期间敌人 AI 还会照常向玩家中心走位,把重叠
"续命"。叠加事实:Godot 的 move_and_slide 对已重叠的双方永不主动分离
safe_margin 0.001),所以一旦嵌入只能靠显式排斥。
2. **修复**MotionExecutor 记录冲刺方向;`_try_restore_collision` 嵌入时
不再干等,每物理帧沿冲刺方向 `move_and_collide` 推 4px240px/s,快于
小怪追击 120px/s),顶到边界墙推不动则反向从入口侧退出;推清后恢复
碰撞。幽灵掩码只剩 world 位,排斥不可能进入地形。"从远侧穿出"的
07-04 手感语义保留;cancel 的非物理帧即时恢复路径不变(有测试钉)。
3. **不动的部分**:敌人互相不碰撞(minion/boss mask 无 bit64,成群穿插是
现状设计);小怪尸体在死亡动画期间仍挡路(存量怪癖);Boss 战前
combat_enabled=false 非实体是有意豁免(test_level1_melee_contact 钉)。
4. 契约测试 test_dash_overlap_separation:嵌入落点 45 帧内推出+恢复碰撞+
从远侧穿出+间距 ≥ 半宽和;干净落点行为与旧版一致。
## 2026-07-06 第六轮修复(Boss→玩家击退 / 视差加强重启)
**语义澄清**:策划三轮反馈的"Boss 击退"实指 **Boss 攻击玩家时对玩家的击退**
(第三~五轮修的"玩家击退 Boss"方向保留不回退)。
1. **Boss→玩家击退不可见的三个根因**:①Boss 近战沿用默认基准 120,普通
连段倍率仅 0.4/0.5 → 滑行 2~3px(重击也只 20~37px);②敌方弹丸基准击退
恒为 (0,0)actors_container 只给敌弹注 speed),Boss 未来相位只用远程 →
整相位击退数学上为零(.tres 里的 0.3/0.5 倍率是死数值);③相机 1:1 跟随
玩家——玩家被击退时自己在屏幕上不动,只有背景平移,小位移完全无感。
2. **修复**boss.tscn DamageEmitter 覆写 `base_knockback = (360, 304.056)`
(初速 ×3 = 距离 ×9):连段 20/32px、重击 190/341px;敌方弹丸生成时注入
`base_knockback = (360, 0)`(不击飞):Boss 射击 11~32px、女巫 8~15px
强弱势归零规则照常生效。Boss 恒强势(is_strong_in_current_time_phase
恒 true),玩家 Hitstun 0.4s 内不能起手/移动,滑行不受自身动作干扰。
调参 knobboss.tscn 的 base_knockback 一处 + actors_container.gd 注入值。
3. **视差加强重启**:0.92 时代偏移峰值仅 ±56px、实战镜头段 ±20px,肉眼难辨
(策划"看不出视差"两轮反馈的真因;上一轮误判为"脚下打滑"而回退)。现
`PARALLAX_SCROLL_SCALE := 0.8`,峰值 ±141px、实战段 ±60px。已知取舍:
步道砖烙在原画里会随镜头轻微滑动(0.8 下奔跑时约 44px/s),策划若不接受
只能等美术拆层(无伤视差的唯一解,接线半小时)。
## 2026-07-06 第五轮修复(击退被反向突进吃掉 / 视差回退)
1. **Boss 实战零击退的真凶:自己的下一拍动作**。裸场景击退管线全通过,但
真实 stage 里 Boss 被击退 3 帧后就照常起手攻击(attack 态还冻结摩擦衰减),
随后动作自带的突进位移接管 velocity 以 -375px/s 冲回玩家——实测净位移
**-24px**(比不击退还近)。修复:**趔趄门禁**——击退残速期
`can_start_enemy_action` 返回 falseEnemyActionDriver 的 BT/谱面双路径
都走此钩子),滑行完整播完 AI 才恢复;小怪加同款钩子。Boss 受击链强制
撤退豁免:`_trigger_hit_chain_escape``clear_knockback_stray()`(新增
motor 公开方法)再起手 boss_retreat_dash,否则 4 连击逃脱被门禁拦死。
真实 stage 探针验证:位移 -24px → **+132px**(≈理论 129.6)。
2. **单层微视差回退**。可行走步道砖烙在关卡原画里,整图 0.92 倍速 =
角色脚下地砖打滑(策划反馈"视差有问题"即此)。逐像素核查后确认横向
分层缝也不可行:油灯底座 y495-520、栅栏柱脚 y485-495、锚徽 y495-555、
步道面 y495-550 相互咬合,任何水平缝都会把显眼物件剪成两截随视差错位;
左右边界石墙纵贯全高更无法归层。结论:**单张烙死的图做不了无伤视差**,
已回退到背景钉死,契约改为"背景不得随相机移动"。真视差待美术按
"天空/远景海船/中景码头道具/近景步道"拆层出带透明通道的 PNG(宽度均
≥2301+视差余量),接线约半小时(每层 Sprite2D + 相机可见中心 × 系数,
勿用 Parallax2D——历史上因 CanvasModulate/wipe 兼容性移除过)。
## 2026-07-05 第四轮修复(Boss 击退加码 ×9 / 小怪击退修复 / 单层微视差)
1. **Boss 击退距离 ×3 → ×9**:首轮 ×3(滑行 43px 级)试玩仍嫌短,策划要求再
×3。`boss.knockback_distance_taken_mult()` 3.0→9.0(初速 ×3)。斩波 Lv1
击退 Boss ≈130px、下砸 ≈634px;嫌多嫌少改这一个返回值即可。
2. **小怪击退修复(结构 bug,与 Boss 同源)**minion.tscn 挂着 MovementMotor
但 minion.gd 零引用——击退初速写入后下一帧就被 AI 接管/清零,且 motor 的
摩擦衰减和假高度积分从不执行。修复:新增 movement_motor 成员;
handle_air_time 无条件委托 motorboss 同款);handle_movement 在相位切换
hold **之后**加击退残速早退(换相位定身优先)。顺带修掉隐藏 bug:垂直击退
曾令小怪 ground_state 永久卡 Airborne,从此 allowed_ground_states 拒绝一切
出招——现在假高度正常积分、落地复位 Grounded。小怪不加距离倍率(基准
15~70px 滑行),策划试玩后如需加码再走 knockback_distance_taken_mult 钩子。
3. **关卡背景单层微视差**ground_background.gd 新增 _process——ArtLayer.x =
基准 + (相机可见中心 − 2047) × 0.08,即背景水平速度为相机的 0.92 倍
PARALLAX_SCROLL_SCALE)。锚点 2047 = GroundAnchor = battle_center =
贴图中心;极限位移 ±56.5px,已验算两端不露边(顺带修掉了原有的极右
16.5px 潜在露边)。用 get_screen_center_position()(含 limit 夹取)而非
camera.global_position,玩家贴墙时背景不跟滑;无相机(headless/菜单)
保持原位。手写每帧位移而非 Parallax2D:历史上 Parallax2D 因
CanvasModulate/wipe 兼容性已被移除过(见下方历史条目)。真·多层视差
(天空/远景/近景分速度)等美术拆层素材到位后再升级。
4. **修复存量 flake**test_rhythm_feedback_and_move_list 的特殊拍断言依赖
"启动后 0.46s 内跑到检查点"RhythmManager 墙钟自启动,_upcoming_beat_index
走实时钟),启动稍慢即越过第 1 拍。按测试纪律钉停时钟(stop_manager),
走确定性 _last_beat_index 兜底。6 连跑验证。
5. 契约测试 test_round4_fixes 钉住小怪水平滑行/残速自清/垂直击退落地恢复、
视差公式与无相机兜底;Boss ×9 更新在 test_round3_fixes。
## 2026-07-05 第三轮修复(标题背景重组 / 判定字样 1s 消失 / Boss 击退 ×3
1. **标题画面重组**:背景弃用烙有 Logo 的整图(start_background_past/future),
改用无 Logo 的干净关卡原画 `assets/art/ground/past/past.png`
`future/future.png`(与关卡内背景同资产),Logo 由独立悬浮 TextureRect
`TITLE_LOGO_RECT` = 390,140,616×300MOUSE_FILTER_IGNORE)叠加。背景轮换
周期改为策划口径"2 秒为单位渐入切换"`TITLE_BG_HOLD_SECONDS` 2.8→2.0、
`TITLE_BG_FADE_SECONDS` 1.6→0.8。渐变期间 Logo/菜单固定,顶部重叠感消除。
难度选择页同步换干净未来原画 + 悬浮 Logo。双背景节点名与 `_title_bg_tween`
变量契约保持不变(test_title_music_and_background 无需改动)。
2. **判定字样 1 秒自动消失**rhythm_track 新增 `JUDGEMENT_VISIBLE_SECONDS=1.0`
倒计时;每次 `_show_judgement_art`(普通判定/锚点 held/TIME SHIFT 三条路径)
重置计时("顶替"语义),到点把 JudgementArt/JudgementLabel 一并隐藏。倒计时
期间不动 modulate/text/scale,与脉冲动画及判定色互不干扰。
3. **Boss 击退距离 ×3**:调查发现 Boss(水平)击退位移实际恒为 0——boss.gd
`handle_movement()` 每物理帧无条件 `velocity.x = 0.0`,击退初速活不过一帧
headless 实测 61 帧位移 0.00px)。修复:MovementMotor 暴露
`has_knockback_stray()`,Boss 在击退残速期跳过清零、交给 480px/s² 摩擦自然
衰减(与玩家同路径,不用被 test_boss_integration 禁止的本地计时器 hack)。
距离 ×3 通过受击方鸭子钩子 `knockback_distance_taken_mult()=3.0` 实现,
CombatManager 按 距离∝初速² 换算为初速 ×√3;只放大水平滑行,击飞不变。
实测 120 初速滑行 14.4px→43.3px。小怪存在同类清零问题(minion.gd AI 每帧
接管 velocity.x),策划未提,本轮未动。
4. 契约测试 test_round3_fixes 钉住以上全部行为(背景资产路径、2s/0.8s 常量、
悬浮 Logo 几何与鼠标穿透、判定 1s 消失+顶替重置、Boss 击退倍率与残速存活)。
## 2026-07-05 第二轮需求落地(剑雨放大 / 敌弹降速 / 菜单组件化 / 结算重做 / 受击朝向修复)
依据策划当日九条答复的最终口径:
1. **剑雨放大一倍(伤害不变)**6 个 jian_yu .tres 的 `range` 140→280(三级判定
仍相同);落剑视觉 strike_count `1+level``2*(1+level)`4/6/8 把)、spread
`90+40*level``120+40*level`Lv3 最远落点 40+240=280 恰与新判定对齐,顺带修复
旧版"视觉铺 250px 但判定只有 140px"的空气剑);BLADE_RAIN_TILE_SPECS 四条目
scale 0.5→1.0raindrop 跟随)。damage_mult/base_cost 一字未动。
test_blade_rain_alignment 的 Lv3 数量断言 4→8。
2. **敌弹降速 80%(斩波不变)**actors_container.gd 新增
`ENEMY_PROJECTILE_SPEED := 416.0`(520×0.8),非玩家阵营弹丸生成时覆写 speed;
玩家弹保持 player_projectile.gd 默认 520。销毁按累计飞行距离,降速不缩射程。
3. **受击朝向修复(策划定案:保持受击前朝向)**:双根因双修——
① combat_manager._apply_knockback 按受击者与 result["from"]emitter 位置)的
x 差把水平击退方向化为"背离攻击者"dx=0 或缺 from 保留旧 +X);result dict
里的 knockback 数值保持 resolver 原值,纯数值契约测试不受影响。
② movement_motor 新增 `_knockback_stray_active` 标记:apply_knockback 置位、
残速衰减到 0 或自主移动接管时清除;set_heading 在标记存活或 life_state !=
Alive 时不改写 heading。玩家/敌人同语义(受击不转身)。
4. **菜单组件化(废弃整图+透明热区)**:新素材套件入库 assets/ui/menu2/
(面板 panel_tall + 按钮底两态 button_base_normal/hover + 文字贴图 13 张 +
Logo title_logo),中文字体 assets/fonts/fzzdhjw.ttf。game_flow_manager 全部
界面改为"面板 + StyleBoxTexture 双态按钮 + 文字贴图/字体"拼装:标题(Logo+
三键+当前难度页脚)、难度选择(简单/普通/困难+开始/回标题+页脚,选中=高亮橙
框)、暂停/确认框/胜负 splash(NinePatch 面板;确认框用 确认/取消 贴图)、
结算(整面板+游戏结算标题字)。按钮保持 Button 类型 + 旧点击矩形兼容坐标测试;
过去/未来场景背景轮换保留。素材里的"结束游戏"按策划答复弃用。
5. **结算重做**:统计标签中文化(最大连击/完美/良好/勉强/失误/锚点成功/相位
切换,fzzdhjw 字体渲染);RANK 只留等级("RANK S",去掉 "(83/100)" 分数),
失败仍不显示 RANK。胜负停留时间 ×3VICTORY_SCREEN_DELAY 1.2→3.6s、
DEFEAT_SCREEN_DELAY 1.6→4.8s;保留两段式(胜负 splash → 查看结算 → 结算)。
6. **胜负合成音效(策划定案:合成音,不引音频文件)**SfxManager 新增
&"victory"C5-E5-G5-C6 上行琶音 1.2s)与 &"defeat"G4-Eb4-C4-G3 分立下行
1.3s,与 &"death" 滑音区分);订阅 EventBus.flow_state_changed,进入
Victory/Defeat 态时播放(voice 继承 PROCESS_MODE_ALWAYS,暂停树可发声);
玩家死亡瞬间的 &"death" 保留叠加。
7. 新契约测试 test_round2_changes(剑雨数值、敌/我弹速、击退方向、朝向门禁、
延迟常量、结算中文与 RANK 格式、难度页脚联动)。
8. 顺带修复的存量测试问题:①四个对难度敏感的测试(startup_fix / time_phase_adapter /
ui_animation_regression / v2_core_contracts)此前隐性依赖 menu 测试残留在
settings.cfg 的 easy 难度,现已显式钉 easy;②三处过时断言改到现口径——相位强势
倍率实为 ×1.4(资源 07-04 校准)、谱面预告标记已有意停用(钉住"不生成")、combo
HUD 脉冲断言改抓命名槽位 Slot1;③test_stage4_movement_physics 的固定秒数等待
会被 ANCHOR_ACTIVE 起手拉伸打败(偶发 flake),改为物理帧轮询。
## 2026-07-05 new1/new2/new3 需求落地(判定门控 / 左侧出生 / 怪物与 Boss 行为)
依据 docs/new1-3.md 与策划当日答疑的最终口径(new1 原文的"零代价无效输入"
已被策划改为"MISS+锁定"混合方案):
1. **判定门控(new1 定案)**RhythmManager 新增 `gate_judged_input` /
`is_input_locked``input_lockout_seconds` 0.5、`consumed_repeat_grace` 1)。
规则:每个节奏点只能被一次有效输入消耗;同拍第二次按键按双击容错忽略;
第三次(或超出 BAD 窗的空按)判 MISS——保留原惩罚(无动作、清连击、四拍
槽写 ∅)并触发 0.5s 输入锁定,锁定期间按键完全无效(不判定、不留痕、不
刷新锁定)。纯判定接口 judge/get_rating_for_time 保持无副作用;门控只作
用于**实时判定**的输入(_ensure_judged 打 `live` 标记),测试/AI 的预置
判定原样绕过。接入点:ActionController.submit_intent 主路径 +
两个蓄力施放路径。锁定横跨时间锚点拍 = 锚点必破(相位切换),设计确认。
test_player_combo_input 顺带修复:每次按键前把时钟钉到新拍心,替代旧的
墙钟判定(~20% flake 消除)。新测试 test_input_gate。
2. **出生点左移(new2**stage.tscn 玩家 (2047,560)→(1180,560),相机初始
(1180,395);开场需向右推进约 1000px 才遇预置近战怪(2200 不变,策划确认
该段路用于熟悉操作)。LevelDirector 的战斗中心/增援锚点不变。标题固定+
背景轮播本就已实现,未改动。基线测试(x1_visual_baseline /
rhythm_ui_layout / boss_integration / level1_melee_contact /
minion_phase_system)中依赖旧出生点的断言改为按门线/停步距离取位。
3. **伤害体系(new3 定案)**:保留"基础攻击 × 相位倍率"。近战小怪基础攻击
翻倍 25→50LevelDirector 按 MinionBehavior.role 配置 emitter.damage
远程保持 25);Boss 近战招式倍率翻倍(combo 0.8/0.9→1.6/1.8;重击保守
放大 combo_3 1.4→2.2、lunging_stab 2.0→3.0,未按字面 ×2 以免单跳 120),
Boss 远程倍率下调 shoot 0.6→0.430×0.4=12 恰合 new3 推荐)、2way
0.7→0.45。
4. **距离参数(new3**:近战怪警戒 420→360、停步 110→120、出手距离
150→120、攻击判定 range 58~74→130(前方 reach≈140);远程怪危险距离
150→220、后撤 190→220、后撤完毕按拍停顿 1 拍;**射程上限压到视距内**
(攻击距离 430→450 ≤ 相机半宽 460——绝不屏幕外开火,new3 的 720 被策划
否决)。Boss melee_distance 135→180、ranged 260→360、dash 360→460。
5. **远程怪受击脱离(策划定案,取代 new3 §5.5 的单次受击)**:连续受击 4 次
(2s 链窗)触发闪烁无敌 0.4s + 跳跃形式脱离(escape_speed_scale 3.2
minion.gd 的 approach_direction 钳制放宽到 ±4);无敌走
FrameCollisionDriver.damage_receiver_enabled(驱动器每帧重申矩阵,直接改
Area2D 会被覆盖);3s 冷却防连续触发;贴墙时沿用穿过玩家身位的反向跳。
6. **Boss 霸体(策划定案,取代 new3 §6.5 韧性系统——明确不做韧性)**
CombatResolver 新增受击方 `shrugs_off_hit(action)` 钩子;Boss 对
base_cost≤0 的攻击恒霸体(伤害照常、打断与击退取消),耗能技能可压制。
所有耗能技能补上击退:dash_slash 1.5、combo_finisher 2.0、ground_smash
2.2(保留原 y1.8 击飞)、zhan_bo 1.0/1.3/1.6、jian_yu 0.8/y0.6;玩家弹丸
在 ActorsContainer 注入 base_knockback(120,304)(原为零)。保留"被连打
4 次后撤逃脱"作为防贴脸兜底。resolve_knockback 顺带修复为跟随伤害判定
通道(good 0.85),对齐 test_combat_manager_resolvers 既有契约。
7. **Boss 相位行为(new3 §6.4/6.7**:远程相位被近身(≤240px)优先
boss_retreat_dash 拉开(后撤无伤害、8 拍冷却);相位切换后停顿 1 拍再
决策(以总线最后拍号为基准,兼容测试合成拍号)。stage.tscn 的 Boss
stationary=true 保持——那是战前木桩状态,BossRoomGate 反锁时已会释放。
新测试 test_new3_enemy_rules(霸体规则/按职责基础攻击/受击脱离)。
## 2026-07-04 playtest pass 2 (dash pierce / anchor presentation / radial phase wipe)
1. **Dash-through actually pierces now.** Two compounding defects: (a) action
displacement speed was computed over the full action length while the
motion only runs from Active start to action end, so every action
under-travelled by the startup fraction — the 260px dash realized ~195px
and frequently ENDED inside the boss; (b) the collision mask snapped back
the moment the action finished, so ending overlapped meant depenetration
shoved the player back out the entry side ("did not pierce").
Fixes: `_action_motion_speed` divides by (action_beats - startup_beats);
MotionExecutor ghosts BOTH directions during dash_through (strips
player_body|enemy_body bits from mask AND layer so neither side's
move_and_slide recovery can shove anyone), and restores collision
deferred — each physics frame it shape-queries the ghosted body layers
and only snaps back once clear of overlap. Outside physics frames
(headless tests) restore is immediate, keeping the existing
test_stage4_movement_physics contract green.
2. **Time anchor presentation follows the upgrade semantics.** The mirrored
red pair converging every anchor beat read as "recolored beat balls",
and at high streak tiers (interval 1) it doubled screen elements. Now:
the beat movers THEMSELVES turn anchor-red when the beat they converge on
is an anchor (screen element count never exceeds the rhythm grid).
Follow-up: the extra "herald" ball that parked above the center emblem was
judged redundant in playtest (the recolored movers + the HUD "ANCHOR Nb"
countdown carry the information) and was removed entirely — time anchors
add zero elements to the track. Held/broken feedback unchanged.
3. **Phase switch is a player-centered radial wipe.** Scene tint now lives
solely on the CanvasModulate (stronger: past warm 1.0/0.94/0.82, future
cool 0.55/0.72/1.0; the color-mapped ground reads as the other map). On
time_phase_changed the world gets the NEW tint instantly and a fullscreen
blend_mul shader multiplies everything OUTSIDE an expanding circle
(centered on the player, radius 0 → screen diagonal in 0.5s) by
old_tint/new_tint — the old scene visibly dissolves outward from the
player, with a bright rim at the wavefront. Works symmetrically in both
switch directions; the wipe center follows the player each frame.
## 2026-07-04 action feel pass (mirroring / air slam / dash derive / charge gauge / dummy boss)
Fixes driven by playtest feedback against the player_tagged author sheets:
1. **Blade wave cast mirroring.** zhan_bo has no displacement and no left/right
tag, so the body never turned while the projectile aimed at the nearest
boss. `_play_action_animation` now faces projectile actions
(`hit_type == projectile` or `projectile` tag) toward
`projectile_direction()`; the whole Visual (body + FX overlay) mirrors
with heading as usual.
2. **Air combo is a ground slam.** The author's 12-Aerial Combo sheets are
byte-identical to 13-Plunging Strike (PREP / SWING START / FALLING [LOOP] /
LAND + FX2/3/4): the move is a forward slam, not an in-air slash. atk_air
gained the falling loop segment + FX3 hold; both atk_air and plunge_start
now arm `_pending_air_slam_land` at active start — the falling pose holds
until touchdown, then a shared `air_slam_land` animation (land.png 6f +
FX4) plays. FX overlay offsets follow the author's bottom-right anchor
rule `offset = (64 - cell_w, -cell_h)` → FX4 (135x166) = (-71, -166).
air_attack_l/r data: move_mult_y 0.2 → -1.6, active 0.5 / recovery 0.5.
3. **[A][sp] derive beats the A-charge.** With hold_threshold at 0.25 beats
(0.125s @120bpm), beat-cadenced [A] then [SP] with A still held had already
entered Charging, so SP was shelved and the A release cast blade rain.
New rule in ActionController.submit_intent: a pressed SP while an
on_release charge hold (A/D) is armed or charging breaks the charge and
falls through to normal resolution — the four-slot window still holds [A],
so [A][SP] resolves the dash (dash_through pierces the boss and hits while
passing). S-charge (on_secondary_key) is untouched.
4. **Blade rain composite.** Falling blades now fall for exactly
BLADE_RAIN_DROP_FALL_TIME (0.24s @1900px/s) and vanish at ground contact
as the landing tile starts (its first frames carry the streak + impact);
tiles are bottom-anchored to the ground line via per-sheet cell_height,
uniform 0.5 scale, spread grows with charge level (2/3/4 strikes).
5. **Charge gauge = cast level.** ChargeComponent's private 1.1s ramp had
nothing to do with the beat-quantized cast level in ActionController.
ActionController now exposes `charge_state()` (MAX_CHARGE_LEVEL 3,
progress_units 0..max-1) as the single source; the gauge renders
level-space (ready == top level), pulses the overlay on level-up, and the
HUD shows "CHARGE LV n / 3" with per-level fill colors. Legacy 1.1s ramp
remains only as a standalone fallback.
6. **Dummy boss for testing.** Boss export `stationary` (attacks but never
displaces: no BT dash/retreat — replaced with ranged pressure — no action
motion, no hit-chain escape). Enabled on the Stage instance only;
boss.tscn keeps mobile defaults so behavior-tree contract tests still
exercise retreat/escape (the point-blank test explicitly un-sets it).
## 2026-07-04 time anchor / dual time phase feature pass
Implemented the full AnchorV1.0 chapter 10-12/17/20 feature set following the
incremental plan in `Fighting_Rthythm_game/docs/时间锚点与双相位架构修改方案.md`
(all names below use its reserved terms: `time_anchor` / `time_phase` /
`attack_buff` / `streak`):
- **New autoloads** (registered after CombatManager): `TimePhaseManager`
(sole writer of `current_time_phase`, broadcasts `time_phase_changed`) and
`TimeAnchorSystem` (schedules chart-explicit + periodic anchors, resolves
held/broken from `judgement_made` facts only, routes `force_time_phase`).
Periodic candidates are computed lazily (origin + current streak tier) and
lock when they enter the 2-beat lead window; explicit chart anchors win
same-beat collisions and restart the periodic count.
- **EventBus** gained `time_phase_changed` / `time_anchor_scheduled` /
`time_anchor_resolved` / `streak_changed` / `attack_buff_changed`.
`judgement_made(quality, offset_ms, beat_index)` was already extended
during migration (7.1-A), so everything here is pure increment.
- **EffectContainer** two increments only: stack-aware aggregation for add
modifiers (`value x stacks`; all pre-existing resources are max_stacks 1,
audited by test_effect_container) and `set_effect_stacks` (plus a small
`effect_stacks` reader). Resolvers/CombatManager/RhythmManager untouched.
- **Player** gained `StreakCounter` (+1 on skill_executed, zero on miss /
chart_reset) and `AttackBuffComponent` (stack storage IS the
`effect_time_anchor_attack_buff.tres` Effect instance: +10% damage_mult per
stack, cap 20, halved floor on every actual phase switch, zeroed on
chart_reset). Damage rides the existing buffs multiplier slot in
`resolve_damage` — the design formula 1 + 0.10 x stacks emerges from the
stack-aware aggregation.
- **Boss** gained `TimePhaseAdapter` with the PastStrong profile
(`resources/time_phase/profile_past_strong_enemy.tres` — past: dmg x1.2 /
taken x0.8, future: x0.7 / x1.3 via `effect_tp_*` resources; the
FutureStrong mirror profile exists for future enemies). Damage-taken
multipliers ride DefenseModifier entries; both phases share all collision.
- **Chart layer**: `ChartEvent.time_phase_mask` (+`matches_time_phase`),
`ChartTrack.time_phase_mask` (authoring default pushed onto `both` events
at load), `BeatChart.initial_time_phase`, ChartRunner trigger-time final
ruling (skipped events are marked triggered and never re-dispatched) and
initial-phase application on ready/reset. stage9_boss_duel gained a
time_anchor track (beats 8 / 40).
- **Dual music layers**: `AudioLayerController` (scenes/audio/) runs two
always-synced players seeded from `RhythmManager.song_position`, crossfades
volumes on phase switch (0.35s), drift-corrects the *presentation* players
only (loop-wrap aware), and mutes the clock stream. Placeholder assets:
both layers use ev_past1.mp3; the future layer goes through a runtime low-pass
bus so the switch is audible until a real Future loop lands.
- **Presentation placeholders per request**: the second map is ground.png
color-mapped (Stage tints the ground sprite + a CanvasModulate, warm past /
cool future, 0.35s tween + white flash under the UI layer); the time anchor
ball is yellow_ball.png color-mapped crimson in RhythmTrack (mirrored pair
converging on the center, grows near the hit point, held → green lock
flash "ANCHOR HELD", broken → magenta "TIME SHIFT!").
- **HUD**: TimePhaseHud (phase name + next-anchor countdown, buff stacks
/20 with percent, streak) and DebugPanel lines for time phase state and
armed/last-resolved anchors.
- **Tooling/tests**: `tools/time_anchor_chart_tool.gd` (validate / generate /
simulate) and seven new SceneTree tests (`test_time_phase_manager`,
`test_time_anchor_resolution`, `test_time_anchor_scheduling`,
`test_streak_counter`, `test_attack_buff`, `test_time_phase_adapter`,
`test_chart_time_phase_mask`). No Godot binary is available on this
machine, so the suite is authored but not yet executed locally.
Open tuning items deferred (doc chapter 12): anchor-specific window, streak
clear on hurt, on_break punishment effects, conditional anchors (v1.5).
## 2026-07-03 body collision audit
Phase 0 keeps the base physics layer names from the migration manual:
`world`, `player_hurtbox`, `enemy_hurtbox`, `player_hitbox`, and `enemy_hitbox`.
The source project still uses root body collision to let the player and boss block each other:
the player body is on layer 2 with mask 5, while the boss body is on layer 4 with mask 3.
To preserve that feel without mixing body blocking into hurtbox or hitbox layers, the target
project reserves layer 6 as `player_body` and layer 7 as `enemy_body`.
## 2026-07-03 phase 4 character asset migration
Phase 4 copies character art only from the curated `tmp/new` source folders:
`player_tagged` is migrated to `res://assets/art/characters/player/`, and `boss_tagged`
is migrated to `res://assets/art/characters/boss/`. File names are normalized to ASCII
snake_case before animation paths are authored.
The player package README is preserved as `credits_blair_ceradsky.txt`; the package is
CC-BY 4.0 and must credit Blair Ceradsky / @MookaTheCaveman. The boss folder still has
no license file in the source material, so boss art is a development asset until the
license is identified and added.
The old jump-height test is superseded by a no-free-movement/no-jump-input regression:
player movement in this migration comes from ActionData-driven rhythm actions only.
## 2026-07-03 post-migration bug-fix pass
A full audit against `AnchorV1.0.md`, the migration plan/manual, and the character/boss
design doc fixed the following migration defects (tests updated in the same pass):
1. Energy scale: player max energy was 10 while skills cost 15-50, so no skill could
ever be paid for. Player/EnergyComponent max is now 100 and the energy bar fills
proportionally.
2. Melee reach: all player melee ActionData had `range = 0`, leaving a 48px centered
hitbox that body-blocking made unreachable. Every melee action now carries a range.
3. Projectile teams: the shared projectile scene was hard-wired to enemy-hitbox layer /
player-hurtbox mask, so the player's blade wave hit the player and never the boss.
`projectile_requested` now carries a context Dictionary (team, action, judgement,
range); ActorsContainer assigns collision layers per team and injects the ActionData
and judgement so damage multipliers resolve. Player gained muzzle spawn methods;
the boss muzzle height was lowered to 55 so bullets actually cross the player hurtbox.
4. Super armor: HealthComponent entered Hitstun on every damaging hit regardless of
`interrupts`; it now respects the defense resolution (death still always applies).
5. Boss behavior tree: retreat no longer has top priority at melee range (combo first,
2-way shot punish when melee is on cooldown, retreat only after both), the ranged
cycle no longer bypasses the 2-way cooldown, and beyond dash range the boss closes
in instead of shooting forever. Hit-chain escape now needs 4 hits, has a 4s cooldown
and only fires while grounded. Chart events take priority: an upcoming chart event
postpones BT decisions and a triggering chart event overrides a BT action.
6. Boss presentation: MotionExecutor is cancelled on action end (no more sliding across
the arena), animation speed_scale is aligned to the action's beat length, and the
boss no longer turns to track the player mid-action.
7. Charge levels: level was measured from the initial key press (always level 3);
it is now measured from time spent in the Charging phase, quantized by
`charge_level_beats` (1 beat per level). Holding A/D during a chain-cancelled combo
step now arms the charge gate too.
8. Combo HUD: enemy ComboWindows no longer broadcast to the EventBus, so the player's
four-slot HUD is not wiped whenever the boss is interrupted.
9. Direction semantics: displacement direction now follows the left/right action tags
(mirror pairs) and falls back to the current heading for forward actions (W launcher
no longer forces the player to face right); plunging strike now actually drives the
fake-height fall (negative move_mult_y maps to height_speed while airborne).
10. Player presentation: hit-stun and death now play (manual sheet animations from
14_hit_stun / 15_death), the plunge has its falling loop, and the charge FX overlay
uses the real charging_up_overlay sheet instead of leftover textures.
11. Rhythm track UI reads BPM from RhythmManager (was hard-coded 80 vs the actual 120).
12. Block: parry reduction is graded by the block judgement (perfect 100% / good 80% /
bad 60%) and only applies to attacks from the front, per AnchorV1.0 section 9.4;
blocked hits no longer interrupt. block_start active window widened to 0.5 beats
(total 0.8 beats = 0.4s at 120 BPM, matching the design sheet).
## 2026-07-03 playtest follow-up fixes (bullets / collision / latency / air lock)
Root causes found during the first playtest and their fixes:
1. **Stuck in the air after W then S.** MotionExecutor converted `move_mult_y` into a
screen-space body velocity; when landing flipped the presentation state to LAND the
attack-state cleanup never ran, so the plunge left `velocity.y = -220` on the
CharacterBody and the body ascended forever (vertical motion belongs to the fake
height system, the body must stay on the ground plane). MotionExecutor is now
horizontal-only, MovementMotor zeroes `velocity.y` on landing and decays stray
horizontal knockback velocity (KNOCKBACK_FRICTION 480) whenever no attack owns it.
2. **Bullets looked wrong.** effect_sheet.png is a 6x2 grid of 32px cells (verified by
alpha scan); the projectile sliced it as 5x2 (38.4px, non-integer) so every frame was
cut through the middle. The projectile now uses 6x2, loops the four flight frames at
16 fps, expires by travel range (default 460px, zhan_bo levels 380/460/540) instead
of a 0.3s animation timer, and flies at 520px/s instead of 1100 so it is readable.
3. **Collision boxes did not match the drawn bodies.** The new art draws both characters
with their feet ~40px above the node root (player idle rows 24-88 of 128; boss rows
16-47 with a -67 sprite offset). All body/hurtbox/hitbox shapes still sat at the old
feet-at-root heights, i.e. ~35px below the visible bodies. Player shapes moved to
y=-72, boss shapes to y=-70, projectile lanes to -70 on both sides. Player waves now
also clear enemy bullets (mask includes enemy_hitbox; enemy_projectiles are freed on
contact) implementing the design's projectile duel.
4. **Blade wave fired away from the boss.** The player heading defaults to left and only
directional attacks turn it; zhan_bo now aims at the nearest live member of the
"bosses" group with heading as fallback.
5. **Latency feel.** anchor_grid halved (0.5 -> 0.25 beats) so on-beat presses reach
Active with no artificial stretch; the four-slot clear display no longer swallows the
next input (it flushes and consumes immediately); agile input flushing enabled in
project.godot; player action displacement now follows the S6 rule (move_mult 1.0 =
100px over the action) instead of a flat 220px/s lunge.
test_boss_integration projectile assertions were rewritten for the new contract
(6x2 grid, flight loop, range expiry, torso-lane spawn heights, moved hurtboxes).
## 2026-07-03 player animation / FX audit vs player_tagged source pack
Audited all 17 tagged action folders against the runtime setup. Root problem: only the
three AnimationPlayer clips (atk_ground_1/2, rising_slash) had FX tracks; every action
running through the manual sheet-animation path had no FX channel at all, and looping
cast animations were being stomped by idle because attack_time_left used the natural
clip length (0.1s for the 2-frame blade rain loop).
- Manual animation system now supports per-segment fps and a parallel FX timeline
driving Visual/FxOverlay. All overlay offsets were computed by aligning the author's
embedded frame-0 idle reference against the idle sheet (method reproduces the two
hand-calibrated offsets exactly): 01/02/03/05 slash (-147,-191), 04 dash (-98,-128),
07 rising (-111,-156), 08 prep ring (-64,-305), 11 cast (-115,-172),
12/13 FX2/FX3 (-107,-198).
- FX wired: atk_ground_3 + combo_finisher slash overlays, dash_slash speed-line overlay
(frame 1 is the author's intentional blank), atk_air FX2, plunge FX2 + FX3 falling
hold, blade_wave cast overlay, blade_rain launch ring (prep overlay frames 17-19).
- Charge visuals split per entry: A/D charge plays the MAGIC CAST prep body animation
(hold last frame) with the sword-ring overlay forming (frames 1-16, hold); S charge
keeps idle stance with CHARGING UP overlay at the author's 50fps.
- Mode C: ContactFxSpawner script added (was a script-less placeholder node) — spawns
the per-attack ENEMY CONTACT bursts on the target for melee and the projectile
contact burst for blade waves. Mode D: ground_smash chains three SHOCKWAVE tiles
along the ground; jian_yu spawns raindrops plus 2-4 RAIN LANDING TILE pillars scaled
by charge level. One-shot world FX use scenes/combat/one_shot_fx.gd.
- Blade wave projectiles now use the author's PROJECTILE sheet (5 frames, 33fps loop);
enemy bullets keep the effect_sheet placeholder.
- Smoothness: attack presentation window extended to the action's beat length (idle no
longer cuts off casts), same-name animations restart on chain (player and boss), and
starting any new action clears stale FX overlay state. Idle now plays all 8 frames
(test updated to [0..7]).
## 2026-07-04 full UI reskin (ui_and_ground asset pack) + harbor stage backdrop
The placeholder rhythm-track art (rod/blue_ball stitching, center.png emblem,
yellow_ball movers) was replaced wholesale by the authored pack in ui_and_ground/,
and the stage gained a three-layer harbor backdrop. Map A (past phase) uses the
purple/gold set, map B (future phase) the red "d" set; the world itself still
renders map B via the existing CanvasModulate color mapping + radial wipe.
- RhythmTrack re-skins entirely from `TRACK_SKINS[time_phase]`
(rhythm_track.gd): background b/bd (single stretched piece — no more rod
stitching, the line sits centered in the frame), long axis line/lined, center
emblem anchor01/anchor01d, movers star/stard, special (time anchor) beats swap
the mover texture to sanchor/sanchord — still zero extra track elements.
- Hit feedback per the art spec: a judged normal beat flips the emblem to the
own-phase anchor02 variant and fires the own-phase c01 flash; a held special
beat flips the emblem to the CROSS-phase anchor02 variant with the own-phase
c02 flash (A: anchor02d + c02, B: anchor02 + c02d). The emblem reverts to
idle after 0.45s. A latch keeps the follow-up judgement readout from
overwriting anchor feedback (held resolutions arrive nested inside the same
judgement_made dispatch; deadline breaks pass an empty judgement dict and
don't latch).
- Beat ticks now pulse the flash at 0.35 peak alpha so full brightness reads as
"you hit something"; test_ui_animation_regression asserts visibility > 0.2.
- Stage backdrop: long.png (sunset sea, Parallax2D scroll 0.12, scaled 1.4378 to
exactly fill the 982px-tall view) and mid.png (lighthouse/ship/crane cluster,
scroll 0.4, scaled 0.92, bases tucked 17px below the sea horizon at y≈383)
render behind the ground scene; both are plain-canvas Parallax2D so the
CanvasModulate phase tint and the radial wipe cover them too.
- ground.png (stone dock with chain railing + end pillars) replaces the old
strip at uniform scale 1.7979: the walk surface in the art (texture y 570)
lands exactly on the gameplay ground plane y=366 and the art spans precisely
boundary-to-boundary (-21.5..4115.5, pillars at the walls). A dark
StreetUnderfill polygon backfills below the art so the bottom of the frame
never shows void, and Camera2D got limit_left/right (-22/4116) so the view
can no longer slide past the artwork at the arena edges.
## 2026-07-04 (later) disaster recovery + WYSIWYG authored view + panel HUD + per-frame collision matrices
Context: at ~05:11 an incomplete Mac zip (Baidu sync had not finished uploading
scenes/assets — the *.baiduyun.uploading.cfg markers) was extracted over the
tree and the merge wiped scenes/, scripts/, tools/, most of resources/ and
assets/. The repo had zero commits, so nothing was recoverable from git. The
tree was rebuilt from four sources: (1) the three session transcripts (every
file ever read/written, replayed Write+Edit chains, most files verified
byte-exact against the surviving .git/index SHA-1s), (2) the old project
(assets, addon, byte-identical UI scenes, the tagged author packs under
assets/art/characters/tmp/new/), (3) ui_and_ground/ + playbossui/ art packs,
(4) the three newer test files carried by the zip (test_rhythm_ui_layout,
test_boss_integration, test_stage4_player_scene), which numerically encode the
newer authored-view state and were treated as the binding spec. A survivor
backup was kept at ../Rthythm_archor_game_survivor_backup_0542. THIS commit
history now exists precisely so this can never happen again.
WYSIWYG (editor == game), per the newer tests:
- Stage exports use_authored_camera_view (default true): the editor-authored
Camera2D pose IS the runtime view — position (2229.63, 97.38), zoom 0.82,
no player follow, no zoom overwrite. false restores the legacy follow rig.
- MainUI moved out of CanvasLayer into the world canvas: Main/UI is a Control
positioned at the camera world-rect origin (1527.191, -297.742) at scale
1/0.82, sized 1152x648 — the editor shows UI over world exactly as rendered.
- Backgrounds are plain Node2D containers (names LongParallax/MidParallax kept
for test paths): long.png sunset at (791.6, -335) x1.25, mid.png regions as
moored ship (1230, -250) and lighthouse (2650, -303); CanvasModulate is
authored in stage.tscn so the phase tint previews in the editor.
- Ground sprite crops to region Rect2(0, 90, 2301, 593), offset (-1150.5,
-480) — walk line stays texture y570 = world y366. Actor grounding is
authored: Visual position (0, 32) with player.gd visual_ground_offset 32.
- Player Visual scale ±2, boss Visual scale ±4 (flip preserves magnitude);
all scaled sprites use TEXTURE_FILTER_LINEAR.
Panel HUD (playbossui pack): StatusBars at (20.74, 35.64) wraps playerui.png
(x1.5) with HealthBar/EnergyBar/ChargeBar overlaying the baked wells
(player_red_ui/player_blue_ui fills); BossStatus right edge 1123.2 wraps
boss_ui.png with BossHealthBar (boss_red_ui fill) + DREAD HARBINGER name;
ComboWindow centered at y 278.64; TimePhaseHud and DebugPanel ship hidden.
Per-frame collision/damage matrices (frame_collision_driver.gd, on player and
boss): every physics tick the driver re-derives all three matrices from the
frame actually displayed —
- body: base layer/mask re-asserted, composed with MotionExecutor ghost bits;
- damage-receive: DamageReceiver rect follows the opaque pixel bounds of the
current frame (per-sheet bounds cached from texture alpha), margins via
hurt_margin, mirrored automatically through the Visual transform;
- damage-send: DamageEmitter only monitors during ACTIVE-phase frames whose
native-forward extent reaches HIT_REACH_GATE (0.72) of the sheet's max —
the hit window is the weapon-extended frames, not the whole active phase;
the shape is rebuilt per frame from pose reach vs action range.
DamageEmitter gained per-swing dedup (_already_hit_ids) since per-frame
windows can re-trigger area_entered within one swing. New headless test:
tests/test_frame_collision_driver.gd (synthetic 4-frame sheet: pose-following
hurtbox, reach-gated hitbox, mirror flip, matrix re-assertion, swing dedup).
Verification pass (9 static-analysis agents over all 39 tests + cross-refs)
confirmed the rebuild and surfaced fixes that were applied:
- grounding: player CharacterSprite/FxOverlay moved to node position (0, 40)
(visible feet at world 398, camera-frame ratio 0.88; FX stays glued since
both sprites shift together and all test-pinned offsets are untouched);
boss.gd gained the set_sprite_height_position override so the authored
Visual (0, 32) survives physics ticks — boss visible feet land at 398 too.
- driver: forward reach is now facing-derived (boss art is native RIGHT-
facing, player native LEFT — the mirror sign of basis_x times heading picks
the correct native side); a late-ACTIVE fallback window guarantees a plain
range hit if no frame of a swing passes the reach gate (art timing can no
longer whiff a beat-anchored swing); frame bounds use per-frame
get_region().get_used_rect() (C++-side, no first-swing hitch).
- test isolation: chart_runner and TimeAnchorSystem resolve bus/manager/clock
via reverse root-child scan, and only the real TimeAnchorSystem autoload
self-drives from the global clock (test instances are inert).
- player.gd no longer contains get_parent() (architecture test contract).
Known pre-existing flake (left as-is, documented): test_player_combo_input
judges through the RhythmManager wall clock headless, so ~20% of beat phase
lands in the miss window and the run can abort; other suites pin judgements.
The `libraries/ =` AnimationPlayer serialization was flagged and verified
VALID (per-library subpath form, matches the editor-saved old project), and
hash-verified load_steps miscounts in old .tres files were intentionally kept
(historical bytes, self-healing on editor save).
## 2026-07-04 final Level 1 and menu UI migration baseline
The playable baseline was restored to the `rthythm_archor_game_player`
reference for camera, HUD canvas, actor/background scale, and authored stage
proportions. Level migration is scoped to Level 1 flow, spawn logic, gate state,
and combat sequencing; it must not resize the player, minions, Boss,
background, HUD, or camera.
- `GameFlowManager` now follows the `Rthythm_archor_game` menu implementation
byte-for-byte and uses the authored menu textures under `assets/ui/menu`.
Difficulty options apply immediately on click, the difficulty page exposes
the reference start/back commands, and no current-value label is drawn over
the command area.
- Stage runtime returns to the player reference view: `UILayer/UI` remains a
CanvasLayer-hosted HUD, the camera starts at `(2047, 395)` with zoom
`(1.25, 1.25)`, and camera follow is retained.
- Root ground anchors for player, the initial minion, and Boss stay at
`y = 560`; the visible sprite feet line is verified at `y = 520` for all
three actors. Boss uses `visual_ground_offset = -40` and the first active
minion uses `visual_ground_offset = -32` so the runtime walk/dash frame lines
up with the same visible baseline without changing actor scale.
## 2026-07-05 charge release facing, blade rain ground line, projectile hit circles
Three combat-feel fixes reported from play: the S-charge release auto-aimed at
the nearest enemy (waves went right whenever a target stood to the right), the
blade rain landing FX rendered 40px below where the player visibly stands, and
enemy bullets carried a hit circle far larger than the drawn bolt.
- Player `projectile_direction` now returns the current facing only (left
heading releases left); the nearest-enemy auto-aim helper was removed along
with the projectile-turn branch in `_play_action_animation` (a release no
longer flips the player toward a target behind them). Regression:
`tests/test_zhan_bo_release_facing.gd`.
- World-FX ground anchoring: actor origins sit on `y = 560` but the visible
feet line is `y = 520` (40px transparent margin under the authored player
frames; minions/boss match via `visual_ground_offset`). Blade rain raindrops
and landing tiles now anchor to `Player.VISIBLE_GROUND_LINE_OFFSET = -40` so
the lowest visible pixel of every landing tile shares the player's visible
ground line instead of sinking into the pier. `tests/test_blade_rain_alignment.gd`
asserts against the constant.
- Projectile collision must stay inside the visible art: `VISUAL_PROFILES`
gained `collision_radius` (bullet 9.0 — the effect_sheet bolt is ~32x18px at
scale 2; wave keeps 32.0, inside its ~66px smallest frame) and
`_apply_visual_profile` duplicates the scene's shared `CircleShape2D` before
resizing so bullet/wave instances never overwrite each other. Layer audit
found emitter/receiver/projectile team masks already correct (player
projectile also keeps enemy_hitbox in its mask on purpose — wave clears
enemy bullets). Melee hitboxes untouched: jian_yu keeps its 140px range,
which stays narrower than the FX spread at every level.
- Known stale test: `test_stage4_player_scene.gd` expects the old 2x-scaled
player Visual with a `(0, 32)` editor feet preview; it fails against the
current 1x `player.tscn` regardless of these changes (verified by reverting).
+11
View File
@@ -0,0 +1,11 @@
节奏输入有效范围规则
节奏条的中心位置为核心判定锚点。
在核心判定锚点左右两侧,各存在一段有效输入范围。该范围可以理解为“中心判定区”。
只有当普通节奏点或时间锚点进入这个中心判定区时,玩家按键才会被视为有效输入,并进入后续的节奏判定流程。
如果当前中心判定区内没有任何普通节奏点或时间锚点,则玩家按键视为无效输入。
无效输入不会触发动作,不会进入 BAD / MISS / GOOD / PERFECT 判定流程,也不会消耗节奏点。
+7
View File
@@ -0,0 +1,7 @@
开始界面标题固定
* 开始界面的背景可以进行循环切换。
* 但游戏标题不参与切换,不跟随背景变化。
* 标题位置始终固定,避免背景切换时导致标题位置发生变化。
3. 玩家初始生成位置
* 游戏正式开始后,玩家角色应生成在地图最左侧。
* 该位置作为关卡起点,玩家从左向右推进关卡。
+569
View File
@@ -0,0 +1,569 @@
怪物与 Boss 行为设定方案
1. 核心原则
* 近战怪:近距离、高伤害、主动压迫玩家。
* 远程怪:远距离、低伤害、保持距离输出。
* Boss:根据相位切换近战 / 远程行为组。
* 强势相位怪物的攻击可以打断玩家动作。
* 弱势相位怪物的攻击不能打断玩家动作。
* 玩家动作带霸体时,不会被怪物攻击打断。
2. 通用行为状态
所有敌人使用以下基础状态:
待机
追踪 / 调整距离
攻击预备
攻击触发
攻击后摇
受击
脱离 / 后撤
死亡
所有攻击必须有三段:
攻击预备 → 攻击触发 → 攻击后摇
状态切换尽量按节拍发生,避免怪物连续无间隔攻击。
3. 伤害设定
3.1 近战怪伤害
近战怪攻击伤害:高
推荐值:20
特点:
* 攻击距离短。
* 命中代价高。
* 强势相位下可打断玩家。
* 弱势相位下只造成伤害,不打断玩家。
3.2 远程怪伤害
远程怪攻击伤害:低
推荐值:8
特点:
* 攻击距离远。
* 命中频率较高。
* 主要制造站位压力。
* 强势相位下可打断玩家。
* 弱势相位下只造成伤害,不打断玩家。
3.3 Boss 伤害
Boss 根据当前相位切换攻击倾向:
近战相位:攻击力高
远程相位:攻击力低
推荐值:
Boss 近战攻击伤害:25
Boss 远程攻击伤害:12
4. 近战怪行为逻辑
4.1 行为定位
近战怪负责贴身压迫玩家。
它不需要频繁后撤,也不需要复杂逃跑逻辑。
4.2 距离规则
警戒距离:360 px
追踪停止距离:120 px
攻击距离:120 px
攻击判定范围:前方 140 px
4.3 行为流程
玩家进入警戒距离
近战怪向玩家移动
进入攻击距离
停止移动
攻击预备
攻击触发
攻击后摇
重新判断距离
4.4 受击逻辑
弱势相位
受到高伤害
可被普通攻击打出硬直
不具备明显霸体
可以被玩家快速击败
强势相位
受到伤害降低
硬直减弱
普通攻击不一定打断动作
攻击触发阶段具备霸体
4.5 墙角逻辑
近战怪被逼到墙角时,不后撤。
处理方式:
如果玩家距离 < 120 px
近战怪优先攻击
强势相位下攻击可打断玩家
命中后对玩家造成小幅击退
推荐击退距离:
玩家被击退:80120 px
5. 远程怪行为逻辑
5.1 行为定位
远程怪负责远距离压制玩家。
它不能被玩家贴身无限连死,所以需要后撤和脱离机制。
5.2 距离规则
危险距离:0220 px
理想距离:320480 px
最大攻击距离:720 px
5.3 基础行为流程
玩家进入攻击范围
远程怪寻找理想距离
保持 320480 px 距离
攻击预备
发射远程攻击
攻击冷却
重新判断距离
5.4 玩家过近时的后撤逻辑
触发条件:
玩家距离远程怪 < 220 px
执行流程:
停止攻击
进入短暂脱离保护
向远离玩家方向后撤
恢复到 320420 px 距离
短暂停顿
重新进入远程攻击循环
推荐数值:
单次后撤距离:220 px
最小后撤后距离:320 px
最大后撤后距离:420 px
后撤后停顿:0.4 秒
5.5 被攻击后的脱离逻辑
触发条件满足任一即可:
远程怪被玩家攻击命中
玩家距离远程怪 < 160 px
远程怪在 1 秒内连续受击 2 次
执行流程:
受击
短暂无敌
后撤 / 脱离
恢复到安全距离
取消无敌
重新进入攻击循环
推荐数值:
短暂无敌时间:0.35 秒
后撤距离:220260 px
脱离冷却:3 秒
限制:
后撤过程中不攻击
无敌时间只用于脱离
不能连续触发无敌后撤
5.6 墙角脱离逻辑
触发条件:
玩家距离远程怪 < 220 px
远程怪背后距离墙体 < 120 px
脱离冷却已结束
执行流程:
短暂无敌
穿越玩家 / 闪避到玩家另一侧
落点与玩家保持约 320 px
短暂停顿
恢复攻击循环
推荐数值:
穿越距离:玩家身后 320 px
无敌时间:0.35 秒
墙角脱离冷却:4 秒
落地停顿:0.5 秒
限制:
墙角脱离不造成伤害
不能连续触发
不能直接接攻击
6. Boss 行为逻辑
6.1 Boss 核心设定
Boss 拥有两套行为组:
近战相位:近战技能组
远程相位:远程技能组
Boss 不采用小怪的弱势相位秒杀逻辑。
Boss 的相位切换主要改变:
攻击方式
攻击距离
攻击力
追踪策略
霸体强度
6.2 Boss 近战相位
定位
主动靠近玩家
使用高伤害近战攻击
压迫玩家站位
目标距离
目标距离:100160 px
攻击距离:180 px
行为流程
判断玩家距离
如果距离 > 180 px,向玩家靠近
如果距离 ≤ 180 px,进入近战攻击预备
攻击触发
攻击后摇
重新判断距离
伤害
Boss 近战伤害:25
打断
近战相位攻击可打断玩家
玩家霸体动作除外
6.3 Boss 远程相位
定位
主动拉开距离
使用低伤害远程攻击
制造空间压制
目标距离
目标距离:360520 px
最小安全距离:240 px
最大攻击距离:760 px
行为流程
判断玩家距离
如果玩家距离 < 240 pxBoss 后撤
如果玩家距离在 360–520 px,使用远程攻击
如果玩家距离 > 760 pxBoss 小幅靠近
重新判断距离
伤害
Boss 远程伤害:12
打断
远程相位攻击可打断玩家
玩家霸体动作除外
6.4 Boss 相位切换规则
当前为近战相位
切换到远程相位
Boss 拉开距离
使用远程攻击组
当前为远程相位
切换到近战相位
Boss 主动靠近玩家
使用近战攻击组
相位切换时不立刻攻击。
推荐流程:
相位切换提示
短暂停顿
调整距离
进入新相位攻击循环
推荐数值:
相位切换停顿:0.5 秒
6.5 Boss 受击逻辑
Boss 不应被普通连招无限压制。
普通受击
正常掉血
播放受击反馈
不进入完整硬直
韧性机制
推荐设定:
Boss 韧性值:100
普通攻击削韧:10
技能攻击削韧:20
时间锚点强化攻击削韧:30
韧性归零时:
Boss 进入短暂硬直
硬直后恢复韧性
推荐数值:
Boss 硬直时间:1.2 秒
韧性恢复值:100
6.6 Boss 霸体逻辑
普通移动:轻霸体
攻击预备:中霸体
攻击触发:强霸体
攻击后摇:霸体降低
硬直状态:无霸体
玩家主要输出窗口:
Boss 攻击后摇
Boss 韧性破防硬直
Boss 相位切换后的短暂停顿
6.7 Boss 后撤逻辑
只在远程相位下启用。
触发条件:
Boss 当前为远程相位
玩家距离 Boss < 240 px
执行流程:
停止攻击
后撤
恢复到 360480 px 距离
短暂停顿
继续远程攻击
推荐数值:
Boss 后撤距离:280 px
后撤后目标距离:360480 px
后撤冷却:4 秒
后撤后停顿:0.5 秒
限制:
Boss 后撤过程中不攻击
Boss 后撤不造成伤害
Boss 后撤不能连续触发
6.8 Boss 墙角处理
触发条件:
Boss 当前为远程相位
玩家距离 Boss < 240 px
Boss 背后距离墙体 < 160 px
后撤冷却已结束
处理方式:
Boss 短暂无敌
穿越玩家 / 跳到另一侧
与玩家重新拉开 400 px 左右距离
短暂停顿
恢复远程攻击循环
推荐数值:
Boss 墙角脱离冷却:6 秒
短暂无敌时间:0.4 秒
落点距离玩家:400 px
落地停顿:0.6 秒
限制:
墙角脱离不造成伤害
不能连续触发
不能直接接攻击
7. 最终规则汇总
近战怪
短距离
高伤害
主动靠近
强势相位可打断玩家
弱势相位容易被击败
被逼墙角时不逃跑,使用攻击反压
远程怪
长距离
低伤害
保持 320480 px 距离
被近身后后撤
被连续攻击后短暂无敌脱离
被逼墙角后穿越玩家脱离
脱离动作不造成伤害
Boss
拥有近战 / 远程两套行为组
随相位切换行为
近战相位高伤害,主动压迫
远程相位低伤害,主动拉开距离
Boss 有韧性和阶段性霸体
Boss 远程相位被近身时后撤
Boss 被逼墙角时可脱离
Boss 脱离动作不造成伤害