M6 追加复盘:UI/UX 批 × promote003 批 × 地图程序化随机生成 × promote004 批

本文是 magic-plant-demom6_review.md(M4–M6 合成/战斗/死亡捡尸)之后的多批追加工作的复盘。 覆盖:M6.5 UI/UX 拍板批promote003 批(M6.6)地图程序化随机生成批promote004 批(大地图定向分区重设计 + 数值重平衡 + 系统层),以及穿插的 bug 修复与数值平衡。 依据:dev_manual.md §2/§4/§5/§7/§9 · content_manual.md §1–§2 · map_progress.md。 地图设计专篇见 m6_map_design.md。 日期:2026-09-03 · 版本:0.1-preview


0. 一图总览(这批改了什么)

三批工作沿「先调体验 → 再修数值/细节 → 最后重构地图」的路径推进,每批都让 preview 更接近「可玩、可看、可调」:

批次 主题 目标
M6.5 UI/UX 一轮拍板 地图从"太小怪贴脸"→四向扩大;开局 9 点;种植 3→6 坑;蘑菇按色区分;背包升级为对话框+拖拽;左上角三格便捷栏(Q/E/R)
M6.6 (promote003) 种植交互 + UI 打磨 + 数值平衡 + 地图场景化 播种取当前选中;便捷栏重排(中央大/两侧小);UI 包边/背景/字体;数值集中配置;地图改手绘场景先跑通
地图程序化批 地图程序化随机生成 简单色块 + 三层 TileMapLayer + FastNoiseLite/Poisson Disk/Drunkard's Walk;固定 seed + 内存态 delta;按 map_progress 重划进度区域
promote004 批 大地图定向分区重设计 + 数值重平衡 + 系统层 400×320 定向多区域(p-轴分区、水做外缘、河/墙门带);合成/升级材料大幅上调;系统层三件套:法术升级 UI(魔药祭坛) + 装备入口(穿戴光明晶石) + T3 无光洞窟
promote005 批 快进指令 × 能力栏背包 UX × T2/T3 密度 × 二批(回蓝/复活/昼夜怪) dev 台新增时间快进(白天/夜晚/HH:MM/分钟);背包「物品栏 ↔ 便捷栏」间加能力栏(5 格、可拖、按 R 施放)+ 3px 金边 + 内部不透明底 + Esc 关三面板;T2 障碍/怪下调、T3 怪上调;二批:青露伞回蓝(灼心芝保持回血) + 死亡复活三维重置 + T2 昼夜刷怪(腐根虫昼/夜行兽夜)+昼夜密度分化(昼 0.3 / 夜 1.7)

结论:preview 已从「能玩的最小闭环」推进到「能玩 + 体验到位的完整闭环」,地图从"一张死地图"升级为"每次开局都能确定性复现的随机地图",再到"沿主轴定向分区、面积精确、门序无环的 400×320 大地图";同时种田=变强(材料需求上调)、法术升级装备穿戴T3 黑暗门槛四条系统线全部串通。promote005 再补上开发者快进、背包能力栏、T2/T3 区域密度三处体验与调试打磨。


1. 里程碑对照

里程碑(本次) 目标 状态 说明
M6.5 UI/UX 体验一轮拍板 见 §2 表
M6.6 promote003 交互/打磨/数值三件套 见 §3 表
地图程序化批 三层 TileMapLayer 随机地图 + 进度分区 见 §4 表
M7 Boss + 结算 + 得分/成就 下一步(封闭地牢 Boss + 入场封门 + 结算累计得分)

这批没有新增里程碑编号,而是在 M6 与 M7 之间做了一次集中的「体验/数值/地图"三条线"的打磨冲刺」,故称 M6 追加


2. M6.5 UI/UX 拍板批

围绕「开局体验不爽 → 改」的七处:

# 诉求 落地
1 地图四向扩大(怪贴脸) WORLD_RECT := Rect2(-520,-300,1040,600)BASE_RECT(-340,-80,240,170);边缘三怪外移到显式坐标,最近距离 >150px 触发仇恨范围;蘑菇从集中撒到 ZONE_MID/ZONE_DEEP
2 开局上午 9 点 game_clock.gd game_min := 9*60DAY_START_MIN=06:00 仍用于睡觉/昼夜分界
3 种植坑 3→6 基地 3×2 网格 6 处 Plot
4 蘑菇按色区分 ItemData.color/CropData.color 双端 @export color;野生/种植/背包图标颜色一致(银光=白、绯泪=粉、月伞=淡蓝),全套程序化 draw_colored_polygon
5 背包升级为对话框 B 开合;ui/inventory_dialog.gd:图标网格 ui/bag_cell.gd + 悬停显示描述 + 6 格便捷栏;拖拽走 Godot 4 _get_drag_data/_can_drop_data/_drop_datamouse_filter=STOP
6 左上角三格便捷栏 ui/quick_slot.gd 轮播 [上一格][当前高亮][下一格]Q/E 左右循环(空槽跳过)、R 食用当前、移动端点中间=用、点两端=切换
7 UI 布局 / 施法键 施法从 E 改 C;HUD/三维条/槽位在代码建(hud.tscn 仅 Control+脚本);三条状态条开局即满的 bug 修复(_ready_fill

额外补了一个交互与背包的联动:食用(Inventory.use_selected)应用 heal_hp/restore_mp/restore_satiety 并扣 1。


3. promote003 批(M6.6)

3.1 种植交互修正

原「种植时随机播种」是 bug——播种应取当前选中修复: - plot.gd_pick_seed_try_plant 改用 Inventory.selected_item()(选中为 null 或不可种 → toast)。 - 新增 Inventory.prune_selected_if_empty()use_selected 末尾复用):播种后数量归零自动清格、跳格。

3.2 UI 打磨

改动
便捷栏重排 SLOT=30(中间不变)/SLOT_SIDE=24(左右缩小)/SLOT_GAP=6 区分主次
背包对话框 StyleBoxFlat 边框 + 圆角 8 + 边框 2;关键_panel = PanelContainer.new() 必须在加 stylebox 前,否则 null 崩溃
炼金合成台 同样的包边/背景,标题 13 / 行 11 / 按钮 12;字体调小
右上角 day/time 从左上移到右上角;三格重排 _mk_top_right

3.3 数值集中配置(回答"哪里调")

所有数值集中在一处 .tres + 对应资源,不散在脚本:

文件 内容 本次调整
resources/game_config.tres 玩家基值/初始蓝/初始饱食度 start_mp=30(原 20)
resources/abilities/bloom.tres 绽华 AoE 伤害/耗蓝 cost 12→8power 2→6
resources/abilities/star_thorn.tres 星刺远程伤害/耗蓝 cost 8→4power=4.0
resources/monsters/night_beast.tres 夜行兽 HP hp=10
resources/monsters/spore_witch.tres 孢子巫 HP hp 12→6
resources/monsters/root_rot.tres 腐根虫 HP hp 16→10

平衡原则:远程耗蓝减半(两次施法仍有余蓝)、Boss 与精英怪 HP 回落(不再"打十几次才死"),普通小怪伤害保持不变——所有改动集中在资源层,改 .tres 即可,不碰逻辑脚本。

3.4 地图场景化(先跑通,后被程序化批取代)

把地图从"代码生成"改成手绘 .tscn,方便直接拖拽调整—— - 新建 level_map.gd(仅存活 CanvasModulate 昼夜 _process/_daylight + _spawn_wild/_spawn_monsters/_rand_in,结构不再代码生成)+ level_map.tscn手写,13 load_steps:4 色块 ZONES/BASE 负 z、3 房、4 围墙 StaticBody2D、6 plot、bed、crafting_station、3 Gate)。 - gate.gd@export data/rect_ready 自初始化。 - 删除 level_prototype.gd/.tscn/.uidtools/build_map_scene.gd。 - 为什么放弃生成器--script 模式不识别 autoload EventBus/InventoryIdentifier not found,生成器无法 headless 验证。

注意:这一步的"手绘 .tscn"后来被本批的程序化随机地图整体替换(见 §4)。level_map.tscn 现只保 Plot×6 / Bed / CraftingStation,地形/边界/门改由生成器产出。


4. 地图程序化随机生成批(本会话)

4.1 需求与两个拍板决策

按 promote003 末条:简单色块 + 三层 TileMapLayer,生成算法 = FastNoiseLite(阈值定草地/农田/水) + Poisson Disk(撒树和矿) + Drunkard's Walk(挖路径),固定 seed,存档只存 seed + delta,批量写入不逐格 set_cell,生成后按 map_progress.md 重划进度区域。

两个关键拍板(AskUserQuestion): - 存档范围 = 内存态 MapState(seed + delta 数据结构),不落盘(符合 dev_manual §9.1「preview 无存档」,为 demo 预留)。 - 分区方式 = 噪声只决定每格地表外观;进度分区用确定性空间规则叠加(基地居中安全区 + 按距基地距离/方位的径向带划分林缘/中环/夜森/洞窟),三块门放分区边界 gap —— 保留 hub-and-spoke 骨架与门序无环。

4.2 新文件(纯数据层可 headless 测 → 运行期层)

文件 职责
scenes/map_generator.gd class_name MapGenerator extends RefCounted纯数据generate(seed)->{ground,obstacle,deco,region_map,gates,seed_used},零 autoload 依赖
resources/region_map.gd RegionMap,进度分区网格(PackedByteArray,BASE/EDGE/MID/DEEP/CAVE)
resources/map_state.gd MapState extends Resource,seed + 玩家改动 delta(内存态不落盘)
tools/map_tileset_builder.gd 程序化色块 atlas → TileSet + 碰撞 + 自定义数据层
scenes/level_map.gd 运行期:建三层 TileMapLayer + 批量写入 + spawn 门/怪/菇 + 昼夜
scenes/level_map.tscn 删手绘地形/边界/固定 Gate,仅保 Plot×6/Bed/CraftingStation
tools/map_gen_test.gd headless 断言(24 checks / 0 fails

映射细节TILE=16GRID=(66,38)(x∈[-33,33)、y∈[-19,19),世界≈1056×608px),BASE_CENTER=(-14,1) 包住 spawn(-180,40),SAFE_R=8。区域阶梯(§1 map_progress):d≤SAFE_R→BASE≤EDGE_R=12→EDGE≤MID_R=19→MID≤DEEP_R=26→DEEP;更远按方位扇区分 CAVE(东 0/1 区)。

4.3 生成算法细节

  • 地表:FastNoiseLite(TYPE_SIMPLEXfrequency≈0.03fractal_octaves=4)→ v<-0.35→WATERv>0.45→FARM、否则 GRASS;再消碎水/成湖(水连通块 <3 回填)、基地安全区强制草地。
  • 路径:Drunkard's Walk 从 BASE_CENTER 挖主路径穿三处门 gap(wobble=0.25),每段末端再散支路(wobble=0.45)。
  • 障碍:Poisson Disk 撒树(density=0.10, min=2,仅 EDGE/MID/DEEP)与矿(density=0.035, min=5,仅 MID/DEEP,避开路径两侧)。
  • 装饰:草点/花/小石 density=0.08
  • 进度分区:径向带 + 8 方位扇区填进 RegionMap;三扇门 gap(magic_gate@EDGE_R / bridge@MID_R / darkness@DEEP_R)铺 PATH 在分区边界缺口。
  • 连通性:BFS 自基地 flood,校验门 gap 可达、每区有可达入口;失败则 seed+1 重试(MAX_TRIES=24),超限走保底图(100% 连通)。

4.4 运行时写入(level_map.gd)

  • MapTilesetBuilder.build() 建 tileset → 创建三层 TileMapLayerGround z=-20Deco z=-12Obstacle z=0),三层共用同一 tileset。
  • spawn:门 Gate.setup(gd, rect)(按 GATE_DEFS 边界 gap rect);怪按 MonsterData.region 分带、远离基地安全半径;野生菇按 day_night 派生子(夜菇→DEEP/CAVE,昼菇→EDGE/MID,高阶夜菇仅 DEEP/CAVE)。
  • 玩家改动 delta(挖矿/砍树/割草等)在 _readyMapState.replay_into 回放。

5. 关键技术点:TileMapLayer 取代 TileMap 的三个坑

本次把整张地图从 Node2D 色块重构到 TileMapLayer,踩到 4.7.2 的三个关键差异(均已修正并写进代码注释):

坑一:TileMapLayer 没有批量 set_cells

TileMapset_cellsTileMapLayer 不存在(只有 set_cell 单格 / set_cells_terrain_connect autotile)。 修正:按 atlas 坐标分组后逐格 set_cell。分组保证色调一致、顺序确定、可复现,且只在 _ready() 集中写一次(非逐帧)。与需求"批量写入"略有偏差——是引擎 API 限制,非偷懒。

坑二:RandomNumberGenerator 没有 shuffle()

Array.shuffle() 用的是全局 RNG(真随机 seed),会破坏 seed 复现。 修正:手动确定性 Fisher-Yates _shuffle(用 _rng.randi_range)。

坑三:碰撞是 TileSet 物理层的事,不是 TileMapLayer 节点的

TileMapLayer 没有 collision_layer/collision_mask(那是 CollisionObject2D 的属性)。赋 layer.collision_layer=... 直接报 Invalid assignment 启动失败。 修正:碰撞由 tile 在 TileSet 哪个物理层建了多边形决定——builder 建两个物理层(PHYS_OPEN=0 可走 / PHYS_BLOCK=1 阻挡),阻挡 tile(water/wall/tree/ore/bush)多边形建在层 1,set_physics_layer_collision_layer(1,1)+...mask(1,1),玩家 mask=3 bit0 命中即挡。另注意 set_collision_polygons_count(0,…) 前必须先 TileSet.add_physics_layer(),否则物理层数为 0 越界。

以上三条都在 map_tileset_builder.gd/map_generator.gd/level_map.gd 注释里留了"为什么这样写",避免未来重踩。


6. 验证方法

  • 纯数据冒烟<Godot>_console.exe --headless --path d:/Repo/waste_aesthetic/magic-plant-demo --script res://tools/map_gen_test.gd== map_gen_test DONE: 24 checks, 0 fails ==EXIT=0。覆盖:网格尺寸 / 基地安全区无危险 tile 且 region=BASE / 每门 gap 连通且为 PATH / 每区可达入口 / seed 复现两版逐格一致 / delta 往返 + replay_into 覆盖格子。
  • 启动冒烟--headless --path <proj> res://scenes/main.tscn --quit-after 30[LevelMap] seed=20260902EXIT=0 零报错;level_map.tscn 单独跑同样干净。
  • 全项目注册--headless --importEXIT=0

关键经验:headless 跑场景必须加 --quit-after N,否则 GUI/窗口模式会阻塞不退(本次已踩:直接 --path 会挂起转后台)。


7. 本次踩的坑(含修正)

现象 修正
#1 架构矛盾 map_generator.gd 同一文件混纯数据 generate() 和引用 Gate/Monster/Content 的运行时 spawn → 整个文件在 headless --script 编译失败 运行期写层/spawn 全搬进 level_map.gdmap_generator.gd 回归纯数据(零 autoload)
#2 TileMapLayerset_cells layer.set_cells(...) 报 nonexistent function 按 atlas 分组逐格 set_cell
#3 RandomNumberGenerator.shuffle _rng.shuffle() 报 nonexistent 手动 Fisher-Yates + Array.shuffle 破坏复现的坑已规避
#4 物理层未建就设碰撞 set_collision_polygons_count(0,…)physics.size()=0 越界 TileSet.add_physics_layer()
#5 TileMapLayercollision_layer layer.collision_layer=... 报 Invalid assignment,启动失败 碰撞由 TileSet 物理层承载,节点不设;构建器 PHYS_BLOCK=1 层承载阻挡多边形
#6 _probe 泄漏 RID SceneTree 冒烟构建 TileSet/TileMapLayer 未释放 → 退出报泄漏 属已知良性(quit() 提前,不走正常 teardown);测试件不用带进关卡
#7 --path 直接跑挂起 窗口模式不退出转后台 --quit-after N

8. 遗留与下一步

优先级 说明
M7 Boss + 结算 + 得分/成就 封闭地牢 Boss(多模式/会躲/入场动画封门)+ 结算累计得分 + 成就——preview 核心闭环仅剩这一环
随机地图的门 rect 是否精确落在分区缺口 GATE_DEFS 的 rect 由 _gap_center 推导,需窗口 F6 人工核对每扇门 gap 是否在对应分区边界、是否明显错位
物理阻挡确认 玩家能否被树/墙/水挡住、能否顺畅走到三门——headless 验不了 2D 物理,需窗口 F6
移动端动作按钮 + 手感 摇杆已可移动;攻击/施法/交互触控按钮、安全区(M8)
美术换皮 占位色块 → Sprite2D/AnimatedSprite2D,只换纹理不碰玩法(§7.4);程序化 TileMap 的 atlas 换正式美术即可
指标 draw calls / script time 暂未实测,M8 打磨时按 §8 预算(draw calls<100、script<4ms)核

9. promote004 批(大地图定向分区重设计 + 数值重平衡 + 系统层)

本批是 promote004.md 需求的落地,分两部分:先数据/功能(低风险),后地图(大改)。地图设计专篇见 m6_map_design.md

9.1 地图重设计(400×320 定向多区域)

  • 网格:66×38 → 400×320(世界 6400×5120px),BASE_CENTER=(0,0)SAFE_R=10region_map.gd 的 W/H/OX/OY 对齐(400/320/-200/-160)。
  • 定向分区:不用"基地居中径向带",改沿 NE↔SW 主轴 p = x − y手摆模板 + 程序化填充q = x + y 的斜带 |q|≤qmax 内才算陆地;qmax 由 _calibrate_partition() 二分使陆地格数 = LAND_TARGET,再按 p 降序把陆地切成 T2(NE)→T1→T3→BOSS(SW)面积精确 = 12/6/9/4 × 当前图(Base 兜底吸收剩余)。俯视呈45° 菱形shots/map_regions.png)。
  • 水做整图外缘_sample_terrain 里所有未被 zone 认领的格 → WATER;另在带缘 NW/SE 撒噪声水袋。不再画径向石墙外缘
  • 门带GATE_DEFSbridge(T1↔T2, 河, dir NE)、magic_gate(T1↔T3, 石墙, dir -NE)、final(T3↔BOSS, 石墙, dir -NE)GATE_TARGETS 定方向落点(右上最多 T2、左下最少 T3/Boss)。_place_gates_and_features 先铺河/墙交界带,_punch_gate 沿 dir 穿 PATH 门洞并记录 rect 供 Gate.setup
  • 路径_carve_paths 从基地沿 Drunkard's Walk(wobble=0.25)挖主路穿三处门 gap,门内直线(wobble=0.0)。
  • 内容规则_spawn_monstersMonsterData.region 分带且成群 3-5_nearest_n);_spawn_wildCropData.region 分 zone 且稀疏(cells/400, 3..28);都避开基地安全半径与障碍格。

9.2 数值重平衡("种田=变强")+ 数据从哪改

合成/升级所需蘑菇数大幅上调,采集几下不够、必须回家种:

改动 现场
配方材料 resources/recipes/*.tres inputs[].count 桥钥=8银芒菇+1魔牙、魔法门钥=8晶铠蕈+1孢粹、光明晶石=8灼心芝+1腐脂、新增 final 钥=8绯泪茸+1洞窟之核
法术升级 resources/abilities/*.tres upgrade_cost 第1级基数=本系菇×4 + 虹髓芝×1;cost_for_rank(rank) 按 rank 递增=越升越贵
作物产量 resources/crops/*.tres yields[].count 3→5

关键:改数值一律改 .tres(Content 懒加载自取),别信 tools/generate_content.gd(已与 .tres 脱节)。详解见 m6_map_design §11。

9.3 系统层三件套(promote004 "直接继续做剩余系统")

① 法术升级 UI(魔法书·魔药祭坛) - ui/ability_upgrade_ui.gdclass_name AbilityUpgradeUI extends Control):只列已解锁且带 upgrade_cost 的法术,显示 Lv/max、分类标签(攻击/疗愈/辅助)、下一级效果(effect_per_level 加成)、升级消耗(含拥有量,如 灼心芝 10/4);点「升级」→ PlayerState.upgrade_ability。 - features/progression/altar.gd(基地紫色祭坛,F 交互)挂进 level_map.tscnEventBus 新增 altar_ui_toggled 信号(逻辑层触发、表现层监听)。 - player_state.gd 新增 upgrade_ability(id):读 cost_for_rank(lvl) 逐项扣 Inventory 素材、ability_levels[id]+1upgrade_cost.is_empty() 直接返回 false——绽华/星刺等基础攻击无素材、不可升(避免免费升级 bug)。

② 装备入口(穿戴光明晶石) - ui/bag_cell.gd 点击 EQUIP 物品穿/脱,已穿戴=绿色方框_draw 加绿色外框),悬停信息行 [装备] 已穿戴(点击穿/脱)/未穿戴。 - ui/inventory_dialog.gd 监听 PlayerState.equipment_changed 重绘;crafting_ui.gd 合成出 EQUIP 物品时 toast 提示「打开背包(B)穿戴」。 - player_state.gd 已有 equip/unequip/is_equipped(equipped=Array[StringName],重复穿戴返回 false)。

③ Stage3-T3 无光洞窟 - features/lighting/darkness.gdclass_name DarknessCave extends CanvasLayer,放 Main 的 UI CanvasLayer 之前)。玩家 region==T3 且未戴光明晶石 → 全屏近全黑(DARK_T3=0.965);戴上DIM_WORN=0.22 微弱洞窟调。 - level_map.gdregion_at_world()/region_cells() + add_to_group("level_map")map_generator.gdworld_cell()cell_world 的逆)。

9.4 修复(promote004.md 末尾两处 + bug_notes_002)

issue 根因 修正
T1→T2 桥解锁后无法通行(路缺区块) _punch_gate 只穿到第一个 after就停——该格仍落在河/墙带内(被水/墙裹住),与 after 侧干地之间隔一格水/墙 → 门洞是"水中小岛",开锁后依旧走不过去;map_gen_test 的 flood 走的是草地而非 PATH,故在纯数据层仍"通过" _punch_gate 改穿到 _is_dry(nxt)(非 WATER/WALL 的干地) 才停,门洞两侧都落在可走地面;_carve_path 主路 + GateStaticBody2D 挡板作唯一阻碍(先连通、用需解锁的门卡住)。新增回归校验:cell→across 沿 dir 全程 PATH 且 across 非水
采集蘑菇无"解锁法术"提示 HUD 的 toast单行 Labelon_interact()emit("解锁能力:%s")emit("采集了「%s」"),同帧后发覆盖先发 → 玩家只看到"采集了" _try_unlock() 改为返回新解锁的能力名,on_interact() 把它拼进同一条文案:采集了「X」,解锁能力:Y(基础攻击绽华/星刺开局已解锁,故只对首次采集的对应蘑菇法术提示)

9.5 踩的坑 / 关键点

  • --script 编译期不注册 autoload 全局名(同 m6 追加 §4 首条):tools/sys_shot.gd/sys_verify.gd 里不能写 c is InventoryDialog 或直接引用 Inventory.xxx(会把依赖脚本拉进编译而 Identifier not found: Inventory)。改走 Variant 鸭子类型root.get_node("/root/Inventory")_hud._dialog;且先 load 主场景再 get_node autoload;动态方法一律 Variant,:= 推断 Variant 会报错要显式标类型。
  • autoload 单例首帧后才可用:在 _ready/_initialize 取会为 null,需在 _process 首帧才 _resolve
  • root 是 Windowget_first_node_in_group 在 Window 上不存在,用 self(SceneTree)的。
  • 免费升级 bug(截图发现):基础法术无 upgrade_cost 在祭坛显示空消耗且可白升——双重修复:upgrade_ability 对空 upgrade_cost 返回 false + _refresh 跳过。
  • 窗口截图别用 --headless:headless 渲染不到纹理,root.get_texture().get_image() 为空。

9.6 验证

  • --import exit 0(无解析错);--headless 主场景引导 exit 0。
  • tools/map_gen_test.gd35 检查 0 败(网格/面积比/基地安全/各门 gap 连通/各区可达/seed 复现/成群 + 新增:门洞穿出交界带落在干地(across 非水且 cell→across 全程 PATH))。
  • 多 seed 复检:12 个 seed × 3 门洞,across 均落干地、桥洞跨河连续(防运行时随机 seed 复现"缺区块")。
  • tools/sys_verify.gd(headless):22 检查 0 败——升级扣 4 风耳蕈+1 虹髓、材料不足 false、无素材基础法术不可升、穿脱/重复穿 false、T3 黑暗未戴 0.96↔戴上 0.33 恢复。
  • tools/sys_shot.gd(窗口模式):shots/sys_upgrade.png(只列蘑菇法术+可负担)、sys_bag.png(绿框穿戴)、sys_dark_no.png(近全黑)、sys_dark_yes.png(恢复视野)目视核验。

10. promote005 批(快进指令 × 能力栏背包 UX × T2/T3 密度 × 二批:回蓝/复活/昼夜怪)

本批是 promote005.md 需求的落地,三组独立改动:① dev 快进时间(开发调试用)→ ② 背包能力栏 + 背包外边框 + Esc 关三面板(content_manual §2.2 治疗 + 4 buff)→ ③ T2/T3 区域密度(T2 怪/障碍过密、T3 怪偏少)。

10.1 快进时间指令(dev 代码)

DevConsole 新增 快进 命令(同样支持 advance / ff,大小写不敏感),底层是 GameClock 两个新方法;核心难点是跳帧也要让 HUD / 昼夜层一并刷新(m3_review 曾踩:只改 game_min 不发射号):

方法 行为
advance_mins(mins) mins<=0 直接 return;否则走 _advance,跨天自动进位并发出 day_started / minute_ticked
set_game_min(target) posmod(target,1440);若该时刻今天已过(t<=game_min)→ 落次日同一时刻 + day_changed;发出 day_started(仅跨天)/minute_ticked,昼夜翻转时发 night_started

_fast_forward(s) 按参数分派:白天/昼/day/晨set_game_min(DAY_START_MIN)夜晚/夜/night/晚set_game_min(NIGHT_START_MIN)HH:MMset_game_min(h*60+m);整数→advance_mins(n);空/解析失败给出用法提示。命令已写入 dev_code.md 命令表。

关键点set_game_mint <= game_min 判断"今天已过",例如当前 20:00(夜)时 快进 白天 会落到次日 06:00 而非法回退——这正是"跨天"语义;posmod 处理负值取模(posmod 而非 %,避免负数错位)。

10.2 能力栏 + 背包 UX

① 能力栏(物品栏 ↔ 便捷物品栏 之间的一行) - ui/inventory_dialog.gd 新增 _ability_row_collectable_abilities() 固定 5 个可习得能力(顺序固定):soil_heal(治疗) / wind_gather / chase_light / rock_guard / ember_will —— 治疗 + 4 buff(content_manual §2.2)。 - 每格 ui/ability_cell.gdAbilityCell):已解锁 → 程序化图标 ui/ability_icon.gd + 金边(可拖 {"ability_id": id});未解锁 → 暗格 + 锁形占位(图钉锁,提示"首次采集对应蘑菇后可用")。悬停经 show_ability_hover 显示等级/描述/解锁来源。 - ui/ability_icon.gd(纯静态画):取 ab.grant_mushroom.color 做主题色,HEAL→圆+白十字,BUFF/AOE→菱形。

② 拖到便捷栏 + R 施放(道具/能力 id 不重叠 → 同一 quickbar 数组) - 设计拍板:物品 id 与能力 id 不重叠,故便捷栏可混存二者,Inventory 解析顺序为 Content.item(id) 先、Content.ability(id) 后。 - inventory.gd 新增 quick_slot_ability(i) / selected_ability()caret()_caret_at_caret_id 改为返回格子 id。 - R 施放use_selected() 先判断是否为能力,命中 → EventBus.ability_requested.emit(ab.id)Player._cast_ability与 C 键同一条施放链,不扣材料、返回 true);否则走原食用路径。event_bus.gd 新增信号 ability_requested(id)。 - ui/quick_slot.gd 新增 ability 字段;_drawitem==null && ability!=null 时渲染 AbilityIcon_can_drop_data/_drop_data 同时收 item_idability_idATTACK 类自动索敌被排除,仅 HEAL/BUFF 可拖入)。 - ui/hud.gd _refresh_quick 按 caret 返回的格子 id 分别解析 item / ability 填入。

③ 背包外边框 + 内部不透明背景 - 用户反馈「原边框太弱/半透明看不清」:inventory_dialog.gd 边框 2→3px 暖金 Color(0.78,0.66,0.40)bg_color alpha 0→1.0Color(0.15,0.15,0.21,1.0))——边框内部彻底不透出地图。

④ Esc 关闭三个对话框 - 背包 / 合成台 / 魔法升级(inventory_dialog / crafting_ui / ability_upgrade_ui统一_unhandled_inputInputManager.menu_pressed()back 动作,映射 Esc)→ close()/_close()。背包标题改为 背包(B / Esc 关闭)

10.3 T2 / T3 区域密度

文件 改动
障碍物 scenes/map_generator.gd _plant_obstacles 改为区域感知:T2 树密度 0.06→0.03、矿 0.08→0.04;T1/T3 保持默认;矿仍只落 T2/T3
怪物 scenes/level_map.gd _spawn_monsters 按区域缩放集群数:T2 首版 ×0.5(减半:原 2 只小怪×满集群 → 过密)、T3 ×1.5(上浮:原 1 只小怪 → 偏少)、T1 ×1.0。用集群数而非包大小,以一个参数同时处理"减 T2 / 增 T3"两个方向clusters = clampi(int(round(min(10, cells/5)*scale)), 1, 15))。二批再细化:T2 内部改按昼夜分化(昼 0.3 / 夜 1.7,见 §10.5),T2 不再用固定 0.5

10.4 验证

  • tools/verify_promote005.gd(headless,--script 模式需 root.get_node("/root/..") 动态取 autoload + _process 首帧再跑):16 检查 0 败——advance_mins 前进/0 无操作、set_game_min 白天/夜晚/跨天(20:00→次日 06:00)、便捷栏能力格命中/quick_slot_item 为 null、use_selected 施放返回 true 且不扣材料、道具格仍走原路、caret 返回格子 id。
  • tools/map_gen_test.gd35 检查 0 败(含 promote004 的门洞回归)。
  • --import:exit 0;主场景引导 exit 0。
  • tools/shot_promote005.gdwindowed,勿 --headless):shots/promote005_bag.png(解锁 5 能力后能力栏 5 格金边、便捷栏第 2 格渲染 chase_light 能力图标、金边 + 不透明底)、shots/promote005_hud.png 目视核验。

踩坑verify_promote005 里曾把「caret 返回能力 id」测成 fail——是断言写错set_quick_slot 会把选中格跳到 index 1,再 cycle_prev 又移回 0),非代码 bug;删掉 cycle_prev 直接查 caret 即 16/16。

10.5 二批:回蓝道具 × 死亡复活重置 × T2 昼夜刷怪

本批是 promote005.md 后半段三类改动:① 没有回蓝道具 → 青露伞改回蓝(灼心芝保持回血)② bug:死亡复活状态栏没重置③ T2 昼夜刷怪(腐根虫昼出 / 夜行兽夜出)+ 昼夜密度分化(白天怪少、晚上更凶)

① 青露伞改回蓝(数据层,.tres) - 起因:吃类道具里没有回蓝的——moon_umbrella(月纹伞) 有 restore_mp=8,但按需求把 dew_umbrella(青露伞) 也改成回蓝:heal_hp 1→0restore_mp 0→8、标签 食_回血食_回蓝resources/items/dew_umbrella.tres)。ember_heart(灼心芝) 保持 heal_hp=1/食_回血(改回血不变)。 - 食用逻辑 autoload/inventory.gd use_selected()heal_hp/restore_mp/restore_satiety → 调 PlayerState.restore_mp,改 .tres 即生效,不碰逻辑脚本。

② 死亡复活状态栏重置(bug) - 根因:autoload/game_manager.gd _respawn_player()heal_hp(max_hp),复活后蓝/饱食度条仍空。 - 修正:复活时三维全部回满——heal_hp(mhp) + restore_mp(mmp) + restore_satiety(msat),三者各自发出 health/mana/satiety_changed → HUD 状态条一并刷新。

③ T2 昼夜刷怪 + 昼夜密度分化 - 谁昼谁夜MonsterData.day_night:0 昼 / 1 夜 / 2 双):root_rot(腐根虫) 保持 0(昼出)night_beast(夜行兽) 从 2(双/始终)→ 1(夜出)。配合 features/combat/monster.gd _phase_active()is_night()==(day_night==1),非活动相直接 visible=false+collision_layer=0+移出 enemies 组 → 昼只见腐根虫、夜只见夜行兽。 - 昼夜密度scenes/level_map.gd _spawn_monsters):T2 内部再按怪物 day_night 分档——昼怪(腐根虫=0) scale 0.3(下调,白天怪少)夜怪(夜行兽=1) scale 1.7(上调,晚上怪多、难度增强);T3 仍 ×1.5、T1 ×1.0(此改动取代 T2 首版固定 0.5)。

10.6 二批验证

  • tools/verify_promote005b.gd(headless):12 检查 0 败——青露伞 restore_mp=8/heal_hp=0、灼心芝 heal_hp=1/restore_mp=0、夜行兽 day_night=1、腐根虫 day_night=0、食用青露伞回蓝 +8 且计数 3→2、复活后 HP/MP/饱食全回满、多 seed 用 Generate 纯数据调 _spawn_monsters → T2 夜怪总数 > 昼怪总数scale 0.3 vs 1.7 生效)。
  • 头less 主场景引导 exit 0;--import exit 0。
  • 踩坑:脚本里 load(main.tscn)--headless --script 下会卡住本 script 的 _process(不触发后续帧)——改不入树即可_respawn_player 只在 current_scene==null 时提前 return,重置三维的语句不在 if p is Player 块内,故用 dummy current_scene 即可测复活;T2 密度则用 lm_script.new()(不入树)+ _spawn_monsters(...) 直接计数,规避 _ready 的 autoload 依赖。

11. 一句话结论

M6 之后的五批工作把 preview 从「能玩」推进到「体验到位 + 地图可复现随机 + 大地图定向分区 + 系统层成型」:UI/UX 一轮解决"怪贴脸/背包太简/无法食用";promote003 解决"播种随机/UI 糙/数值硬";程序化随机地图把「一张死地图」升级为「空地上用噪声/泊松/醉汉踏步长出的三层 TileMap 地图」;promote004 再把它升级为"沿主轴定向分区、面积精确、门序无环的 400×320 大地图",并补齐"种田=变强(材料上调)+法术升级+装备穿戴+T3黑暗门槛"四条系统线promote005 补上"dev 快进 + 背包能力栏(治疗+4buff) + T2/T3 密度 + 回蓝道具/复活重置/昼夜怪密度"数处调试与体验打磨。至此 preview 核心闭环只剩 Boss+结算(M7) 一环即可收官。