geometry-tower-defense 开发日志(2026-03-12 ~ 2026-04-30)
一、本次范围
这个周期做的事比较杂,但主线很清晰:把之前散落在各处的随机掉落、奖励生成、商店产出和事件奖励统统收束到可控的模块里,同时顺手把战斗效果的参数传递方式整个做了现代化改造。另外还上了新输入系统,补完了仓库/商店的买卖逻辑,重新盘了一遍数据表,并且尝试了一套 AI 开发工具,最后又把它请了出去。
二、核心改动概览
1. 掉落与道具生成系统重构
- 新增
InventoryGenerationComponent,在GameEntry里注册成全局入口,专门负责所有局外资源(金币、防御塔组件)的随机生成。原先EnemyDropResolver的这部分职责被它完全替代。 - 商店物品、事件奖励、3 选 1 奖励候选这些需要“从池子里按规则摇出组件”的场景,全部改成委托
InventoryGenerationComponent,不再各自写重复的掉落逻辑。 - 随机数引擎从
UnityEngine.Random换成了System.Random,并引入了显式的RunSeed和NextDropOrdinal/NextRewardOrdinal机制,保证同一种子下掉落结果可复现,同时也及时修了一个因为忘记重置序号导致多场战斗掉落异常的 bug。 CombatSettlementService里原来写死的奖励计算常量被清掉,新增独立的CombatSettlementCalculator,结算奖励和掉落生成彻底解耦。
2. 战斗效果系统接口重构
AttackPayload多了SourceEntityId和ProjectileEntityId,ShooterMuzzleComp在实例化子弹时就把这些实体 ID 填进去,给后面做追踪、特效或者成就系统铺路。- 新加
HitContext结构体,把原先散落在各处的hitContext、targetCurrentHealth等参数包成一个对象。NumericTagEffectHandler和EnemyStatusTagEffectBase的Apply方法都改成只收一个HitContext参数,减少参数列表带来的耦合。
3. UI 与结算流程重构
- UI 控制器的 Context 组装逻辑被拆成独立的
ContextBuilder文件:新增CombatFinishFormController.ContextBuilder.cs,原来的RepoFormController.ContextBuilder.cs也挪到了统一的目录下。 CombatFinishFormController去掉了不必要的CloneTags调用,直接引用原始数据,省掉一些内存分配。- 结算界面实现了描述文本的滚动展示,长文本事件奖励或者结算说明不会再把界面撑爆。
4. 商店与仓库功能扩展
- 新增
PlayerInventoryTradeService,把组件购买逻辑集中管理。 PlayerInventoryComponent里加了TryPurchaseComponent和TryDisassembleTower,出售组件和拆卸防御塔的功能终于通了。ShopGoodsBuilder重构,去掉原先硬编码的循环组装方式,改为委托InventoryGenerationComponent生成商品列表。
5. 事件系统补全
- 事件数据表
Event.txt更新了几个具体事件描述和选项,比如下注、交换组件、黑暗交易等。 EventNodeComponent不再用简单的随机抽选,而是维护一个_activeEvent状态,为后续事件链和多步骤交互留了空间。InventoryGenerationComponent新增BuildEventRewardComponents,能按稀有度生成事件奖励组件,事件奖励不再只能给金币。
6. 数据表大规模调整
- 复合组件定义:
BaseComp.txt、BearingComp.txt、MuzzleComp.txt里增加了 id 4、5、6 的行,都是带有交叉元素标签(如 Fire + Pierce、Ice + Execution 等)的二合一组件,为后续 Tag 组合玩法铺路。 - 掉落池重构:
OutGameDropPool.txt几乎重写,旧表按稀有度拆分多行的设计被替换为更紧凑的地图/阶段配置格式。 - 清理:
Entity.txt里移除了TestMuzzle、TestBearing、TestBase这几个测试实体,数据表里不再残留早期试验痕迹。
7. 输入系统迁移到 New Input System
CombatSelectInputService和MapEntity的鼠标输入检测从旧的Input.GetMouseButtonDown/Input.mousePosition改为Mouse.current。ItemDescForm也引入了UnityEngine.InputSystem命名空间,为后续可能的多键位或手柄操作做准备。
8. 调度器上下文简化
CombatSchedulerRuntime中移除了RunId、RunSeed、NodeId等独立字段,全部收到NodeContext里,减少参数传递的维护负担。CombatNodeComponent和CombatScheduler的启动入口不再需要显式传一堆 run 相关参数。
9. 开发工具配置变动
- 4月30日先引入了
.claude/settings.local.json和.omc/skills/adopt/SKILL.md,试探性地把 AI 辅助开发流程集成进项目。 - 但实际使用一段时间后感觉效果没达到预期,而且不同本地环境下的状态文件会造成不一致,维护成本开始反超收益。于是干脆把所有相关配置和
.idea下一些 IDE 个人文件一起做了清理,.gitignore里也加上了/.omc,防止以后本地工具状态意外入库。后续对开发工具的引入会更谨慎,优先评估团队协作兼容性和长期维护成本。
10. 防御塔组装数值修正
PlayerInventoryTowerAssemblyService中防御塔数值计算从baseValue + perLevel * i改成Mathf.Max(0, baseValue + perLevel * i),堵上了升级时属性变为负数的风险。
三、设计决策与问题反思
为什么要将掉落的职责收拢,但又没有把所有东西都塞进一个“厚组件”
这次重构最纠结的一个点就是 EnemyDropResolver 的去留。它原本把局内 coin 掉落和局外组件/金币奖励揉在一起处理,看起来职责单一,实际上每次调用时调用方都得区分上下文,内部逻辑也拧巴。我们梳理了一下所有掉落来源后发现,局内 coin 是战斗过程中即时结算的,和战斗波次、敌人类型强相关,而局外资源是战后或事件后的二次投放,两者的时机、配置表结构、随机种子管理方式都截然不同。硬放在一个类里表面上是单一职责,其实反而让两个不相关的概念被迫共享同一套代码路径。
所以最终的决定不是“把掉落、商店、奖励生成全塞进一个组件”,而是按资源类型拆开:局内 coin 直接由 CombatScheduler 在战斗结算时轻量地处理,局外奖励(金币、组件)的生成则收敛到 InventoryGenerationComponent。后者再通过内部的 DropPoolRoller、ShopGoodsBuilder 等子模块来处理商店与事件奖励,整体保持一种“协调者 + 多个小策略”的形态,而不是一坨巨大的 God Component。
HitContext 替换零散参数的推进策略
这次效果接口签名大改(从 (HitContext, targetCurrentHealth, ...) 变成只有一个 HitContext),当时确实担心会引起大面积回归,所以我们提前 grep 了一遍所有引用点,确认涉及的处理器的数量可控,且它们本身就集中在同一批重构的范围里,没有隐藏在犄角旮旯的古早代码。改动时是一次性把所有相关的 Apply 方法都改掉,没有采用适配器模式或者逐步废弃旧接口,因为整个改动面非常集中,回归风险小。事后验证也证明了这一点,没有发现因为这次接口变更导致的额外 bug。
数据表破坏性变更的风险意识
OutGameDropPool.txt 几乎是重写级别的改动,这意味如果线上有保留旧存档或者热更流程依赖旧行 ID,就会直接炸。目前我们在 Debug 阶段直接在开发期就把表清掉重来,但之后需要认真考虑加一层简单的数据迁移或版本号校验,避免未来热更时掉进坑里。
CloneTags 去掉后的潜在副作用
CombatFinishFormController 里去掉 CloneTags 是一次直接优化,但原来克隆数据的动机很可能是某些地方会修改数据而不想在原对象上产生副作用。虽然目前看所有使用场景都只是只读展示,但后面如果有人在 View 层写回数据,可能会踩中这个地雷。目前无风险,但值得在代码审查里留意。
四、后续关注点
- 掉落与奖励系统的自动化测试:覆盖不同种子下掉落物的一致性、仓库满溢处理、事件奖励生成边界。
- HitContext 的全面验证:确保所有效果处理器都已迁移完毕,尤其检查是否有从外部伪造旧参数结构的残留代码。
- 新输入系统的平台适配:目前只替换了鼠标相关调用,触屏或手柄输入路径需要补齐,避免后续多平台构建时行为不一致。
- 数据表热更新机制:确保 DropPool 表变更后重载正常,不会因为缓存旧数据导致配置不生效。
- 环境清理后的协作规范:
.omc等本地工具目录虽然进了 gitignore,但如果团队中有人依赖这些工具,需要同步说明工作流,避免环境差异带来的困惑。
注:本文由模型
unknown生成(草稿与终稿同模型)。