当前位置: 首页 > news >正文

当“肉鸽抽卡“遇上“自走棋“:一款三国题材手游的战斗内核设计

当"肉鸽抽卡"遇上"自走棋":一款三国题材手游的战斗内核设计

本文基于项目内部技术分析整理,面向团队内部/技术社区分享,聊聊一款"肉鸽 + 自走棋"手游(内部代号小兵2)在战斗模拟、热更新适配、数据驱动几个方向上的具体设计取舍。


引言:这是一款什么游戏

如果用一句话概括玩法:每一回合结束后随机三选一抽卡,逐步拼出自己的三国阵容,然后交给一个"自动战斗黑盒"去演算胜负

这个组合听起来很常见——TFT 的抽卡逻辑 + 自走棋的自动战斗——但真正做起来,会遇到几个绕不开的工程问题:

  1. 战斗过程要不要能回放?要不要考虑未来上服务器强校验
  2. 卡牌效果(Buff)有几百个,新增一个效果的成本能不能做到"加个文件就好"?
  3. 项目要接热更新(HybridCLR),那些"扫描所有类"的反射代码在热更程序集里还能用吗?
  4. 没有真人对战服务器的情况下,PVP 怎么做?

下面就按这几个问题,把项目里对应的设计逐一拆开看。


一、战斗内核:把"战斗"做成一个纯 C# 黑盒

1.1 为什么要"纯 C#、零 Unity 依赖"

项目把整个战斗模拟单独抽成了一个命名空间BattleCore,规则只有一条:不依赖任何UnityEngine的 API(除了极个别编辑器工具代码)。

这么做的直接收益是:

  • 战斗逻辑可以脱离 Unity 引擎单独跑单元测试、单独编译;
  • 只要输入相同,理论上可以把这份代码搬到服务器上原样跑一遍,用来做防作弊校验或者多人对战的权威判定;
  • 逻辑层完全不碰渲染,View 层只能"读事件",不能反向影响逻辑——架构上天然杜绝了"表现层偷偷改数值"这种糊代码方式。

架构长这样:

外部(View) ──> BattleDirector 播放节奏控制:Play / Pause / Fast / Skip │ 按固定步长 LogicDt = 0.05s(20Hz)驱动 ▼ BattleSimulation 主循环,Tick(dt) → 产出 List<BattleEvent> ┌──────────────┼───────────────┬─────────────┐ ▼ ▼ ▼ ▼ SquadLogic[] UnitLogic[] System_* BuffManager (小队/战术) (单位状态机) (网格/避障/投射物) (卡牌Buff)

BattleDirector只负责"播放",真正跑逻辑的是BattleSimulation。它每一帧只做一件事:推进一个固定步长,然后把这一步发生了什么打包成事件扔出来(近战命中、投射物发射/命中、单位死亡、战斗结束……),View 层拿着事件去播动画、放特效,逻辑层自己完全不关心。

1.2 主循环:10 步走完一帧,顺序不能乱

1. 重建空间网格 所有存活单位按位置重新插入网格,供索敌/查询 2. 更新 Buff 限时 Buff 倒计时、到期还原 3. 小队 Tick 统计攻击次数变化,触发事件供 Buff 监听 4. 单位决策 索敌、预约攻击插槽、算出"期望速度"(还不移动) 5. ORCA 求解 所有单位注册为 Agent,求解避障后的真实速度 6. 应用速度 + 行动 真正位移;处于攻击状态的单位推进攻击计时 7. 硬分离(PBD) 3 次迭代消除残余重叠,兜底 ORCA 8. 投射物 Tick 移动、命中检测、结算、销毁 9. 收集死亡事件 Dead → Release,发 UnitDead 10. 结算胜负 任一方全灭即结束

这个顺序背后是一个很朴素但容易被忽略的原则:"决策"和"移动"必须分两步(第 4 步只算期望速度,第 5、6 步才真正求解和位移)。如果决策阶段直接改位置,ORCA 的输入数据就会在计算过程中被污染,避障效果会变得不可预测。

1.3 确定性:所有随机数都要能"对得上"

自走棋战斗一旦要支持回放、要支持"PVP 用别人的存档数据离线重演",确定性就是硬指标——同样的输入,必须永远算出同样的结果,不能有一丝浮点误差或者随机数错位。

项目里的做法:

① 自研PredictableRandom,PCG32 算法,配合Fork()做分层派生:

Sim.Fork() → Squad.Fork() → Unit.Fork() → Buff.Fork()

每一层拿到的都是从上一层"分叉"出来的独立随机流。这样做的好处是:某个 Buff 内部多消耗了几次随机数,不会导致后面别的单位的随机结果跟着错位——如果全局共用一个

http://www.jsqmd.com/news/1296849/

相关文章:

  • 计算机毕业设计之20222私人小医院病历管理系统
  • ffmpeg-static 跨平台多媒体处理架构解析与技术实践
  • 7步掌握KMS智能激活工具:Windows和Office永久激活完整方案
  • HsMod终极指南:55项功能全面优化你的炉石传说游戏体验
  • 英雄联盟智能助手Seraphine:告别繁琐查询,专注游戏体验的终极解决方案
  • 低空监管全面重构,在沈阳学无人机,选对路子比盲目考证更重要!
  • 数据汇聚与“一图可视”,打破信息孤岛的治理逻辑
  • 如何高效使用直播录制工具:DouyinLiveRecorder专业用户指南
  • 如何掌握Magisk实战:Android系统Root与定制的完整进阶指南
  • 【万字文档+源码】 基于springboot+vue学生信息管理系统-可用于毕设-课程设计-练手学习-学习资料分享
  • Python批量巡检系统资源将结果保存到文件
  • LLM微服务架构评审全链路解析,从Prompt注入到推理延迟的11个关键审查节点
  • Akagi麻将AI助手:3分钟学会用AI提升麻将水平的终极指南
  • 江西省赣州市会昌县君和小镇,三层住宅楼梯中间切割改造,观光曳引龙门架家用电梯落地案例
  • 突破Docker Hub限制:awx-on-k3s私有容器registry部署与配置
  • C#方法:从声明到调用指南
  • 城市生命线采购转向软硬一体,风险评估与监测系统闭环落地
  • Hello,World! [从 0 开始的 C++ 之旅 1.1]
  • 全面掌握NBTExplorer:高效编辑Minecraft游戏数据的跨平台NBT编辑器
  • Zotero插件市场:如何用3个步骤彻底改变你的学术工具管理体验?
  • svmjs实战教程:用JavaScript实现异或问题分类,掌握SVM训练技巧
  • 智能体面试准备(十一):Agent 评估体系——轨迹、工具选择与成功率的量化方法
  • 2026年旧衣服回收能当场拿到现金吗?上门回收流程与平台推荐对比 - 快递物流资讯
  • 揭秘VengeanceUI核心技术:GSAP动画与Three.js 3D效果的完美结合
  • 如何快速安装BetterNCM插件管理器:网易云音乐PC版终极增强指南
  • 从ChatGPT到Codex:AI开发为什么正在进入多Agent协作阶段?
  • 沟槽MOS(Trench MOSFET)——把电流“直译“成效率的秘密
  • IoTaWatt Open WiFi Electric Energy Monitor:打造智能家庭能源管理系统的终极指南
  • 免费解锁9大网盘下载速度!网盘直链下载助手终极使用指南
  • 深入理解C++ const:从语法到实战的常量正确性指南