本文是「AI 原生游戏开发」系列实战篇。六月的立项篇把”将 FPS 改造为 TPS”列为阶段二的第一个论点验证,并承诺”完成即如实记录完成,受阻即如实记录受阻”。一个多月后,这件事做完了——这两篇是它的交付实录。
一、交付实录:这次要记录的是什么
TPS 相机这个题目,拆开是两个维度。
空间维度(机位与构图):相机架在哪、画面里看到什么。臂长多少、肩偏几厘米、角色占画面多大比例、地平线压在哪条线上。这些参数每一个都直接决定”这游戏看起来专业不专业”。
时间维度(过渡与节奏):状态切换用多长时间、什么节奏。举枪多快推近、开镜是瞬切还是渐变、急停时画面怎么收势。这些决定”这游戏玩起来跟手不跟手”。
上篇讲空间维度,下篇讲时间维度。但在进入正题之前,先把这两篇文章的论点亮出来,因为整个改造过程就是围着它转的:
体验 = 功能 × 数据 ← 测量。
这个式子里有一条容易被误读的分界,先讲清楚:它跨了两个不同的世界。
- 运行时系统里,只有两层:功能(机制,无口味):三段机位状态机、瞄准层混合器、过渡定时器、跟随弹簧,每个都是”通道”,自身不含任何数值判断;乘上数据(数值,全外置):构图表、基线配置、弹簧参数、一排控制台变量,策划可改、每武器可差异化。游戏跑起来就这两层,没有第三层。
- 测量不是运行时的一层,它在开发流程里:测量 → 校准 → 数据:判读参照录像得到目标值,反解成引擎参数,最后固化进数据层。它是一条把”我想要的手感”翻译成”表里的一个数”的产线,产线交付完就退场,不进游戏。
功能与数据分离是工程学老路,不新鲜。新鲜的是那条测量产线:以前”手感对齐”靠老师傅坐在机器前手调玄学,这次靠一批参照录像、逐帧判读和一组现场写出来的测量仪器,把”像不像”这个主观问题变成”差多少个百分点”的读数——每个进表的数字都有出处。这正是立项篇给 AI 定的三个角色之一:”参照对齐、机器采数、人来评估”的落地形态。
但有一条边界得先钉死,它贯穿全文:测量降低的是”验证成本”,不是替代”体验判断”。它让”这一版比参照差多少”从一晚上的手调变成几分钟的读数,但”差成这样能不能接受、要不要跟”始终是人的裁决。下文会看到不止一次:数字说”落带”、眼睛说”不对”,最后眼睛赢——测量把选项摆到台面上,拍板的仍是人。
AI 在这三层里各占了什么位置、人在哪些位置不可替代,证据链会贯穿两篇文章,最后在下篇结尾统一回收。
二、蓝图与现实:与五月那篇的对账
动笔之前得先处理一件事。五月中,我们发过一篇 TPS 相机的文章——系统考古、设计原则、七步开发计划。一个多月做下来,实际路径和那篇蓝图对不上。假装那篇不存在最省事,但这个系列承诺过如实记录,而”蓝图和现实的偏差”恰恰是 AI 原生开发最值得记录的东西。对账,逐条来。
兑现了的:
- 弹簧臂 + 数据驱动的方向。三段机位最终就叠在那套按移动状态取值的基线配置上,而且走得更远——构图、时长、FOV 全部进了每武器一行的 CSV 表;
- 五条设计原则。特别是防晕铁律(旋转方向永不加延迟)——后来在标杆作的转镜录像里得到实测印证:转视角零迟滞,我们的原则和它的实现完全一致;
- “AI 理解系统”的价值。后来的后坐力四层配置链路、开火震屏系统的门禁侦破,全是考古学的胜利,这个论点站住了。
被现实修正的:
- 七步直线计划 → 测量驱动循环。五月的计划是七个步骤排成一条线。实际路径是一个循环:采参照录像 → 逐帧判读 → 定值 → 落地 → 实测打回 → 修正。那份计划里没有”录像”两个字,而实际上录像成了一切数值的权威——计划里最重要的东西,是当时还不知道自己需要的东西;
- “理解系统”不够,还要”测量目标”。五月那篇隐含的假设是:看懂了系统结构,就能改出想要的手感。实际的教训是:手感差距全在数字里,不量化的话,连”差在哪”都说不清楚。看懂系统只回答了”能改什么”,量化参照才回答”改成多少”;
- 具体设计被推翻。那篇当案例讲过一组 0.1 秒/0.6 秒的不对称切换时间,后来被实测数据整体替换成 0.20/0.25/瞬切的体系(细节在下篇);文中花大篇幅盘点的修饰器堆栈,实际改造中几乎没动——主战场在弹簧臂层和数据表。
更该追问的是:为什么当时看不见?五月写蓝图时,参照素材一帧都没有、判读工具一件都不存在——”量化对齐”在当时的成本约等于无限大,方法论自然滑向了成本最低的路径:读代码、做推演、写计划。转折点不是谁想通了什么,而是测量的成本被大幅压低了:AI 把录像抽帧、网格叠加、逐帧读数、公式拟合这些过去要专人干几天的活压成了几轮对话,测量从”奢侈品”变成”日用品”,蓝图方法就自动退位了。工具的成本结构变了,方法论跟着变——这可能是 AI 原生开发最底层的一条规律。
对账的结论:
蓝图的作用是让你知道该往哪测;测量结果才是定稿的权威。五月那篇看懂了系统,这两篇看懂了目标——中间隔着一套量化判读工具箱。
三、三段机位:三个状态
先交代骨架。改造后的相机是三段机位:
- 腰射(常态):过肩视角,相机离得远,视野开阔(观察态);
- 肩射(按住右键):相机快速推近,角色让到画面一侧,准星居中(交战态);
- ADS(中键,开镜):复用既有的第一人称瞄具视角(精瞄态)。

结构上有个关键取舍:ADS 不做第三人称贴瞄具的假开镜,而是直接复用工程里成熟的 FPS 开镜路线。弹簧臂只管腰射和肩射两个构图,ADS 期间它退到幕后当”回落基座”。这个决定省掉了一整套镜头细节的重复建设,也留下了一处隐患——下篇讲 ADS 硬切时会展开。
三个状态、两个可玩弹簧臂档位、加一套复用的 FPS 开镜——这是骨架。真正难的不是”有哪三个状态”,是”玩家怎么在它们之间切”,这一块单独一节。
四、输入模型:一天翻两版
状态机画在纸上很干净,落到手柄和鼠标上就变成了另一个问题——而且这个问题一天之内让我们推翻了两版设计,每一版在推翻之前都看起来无懈可击。之所以要完整记录,是因为它印证了一件反直觉的事:状态机之间怎么切换,是产品模型问题,不是代码问题——写代码那部分从来没出过错,出错的全在”以为玩家想要什么”。
第一版:”来源锁存”。设计逻辑很顺:中键进开镜后,系统记住”你是从腰射还是肩射进来的”,退出时原路返回。状态机干净、代码好写,一个下午落完。实测第一轮就暴露了问题:玩家对”退出去哪”的预期跟”从哪进来”根本不是一回事:从腰射直接开镜的玩家,退镜时手还按着右键,预期显然是回肩射继续交战,而不是被”锁存”送回腰射。设计者视角的对称美,在玩家视角是莫名其妙。
第二版:右键点按切换。改成点按举枪/再按放下,中键行为随之简化。测出一个更深的问题:手感不可复现——同一套操作,时而顺畅时而卡滞。排查许久,根源在玩家设置里藏着”点按开镜/长按开镜”两个模式,工程里所有瞄准输入的行为都随这个设置漂移。我们在 A 模式下调的键位,用户机器上是 B 模式,两个人永远测不出同一种手感。更严重的是程序化切换状态时,合成按键事件在点按模式下语义完全不同:”松开”在那个模式里是空操作,一条自动化链路因此静默卡死在开镜状态。
终版:三条硬规矩。右键长按举枪,钉死:代码里直接固定住开镜方式,不再读玩家设置,把这个隐藏维度从此消灭;中键是带记忆的模式开关:只在瞄准中生效,肩射⇄ADS 实时切换,且系统记住你上次的选择:习惯开镜的玩家每次举枪直接进镜,习惯肩射的玩家永远停在肩射;松开右键永远回腰射,模式记忆不清零。另配一条分段规则:腰射直接按中键开镜时,不做瞬间切换,而是快速端枪推近、到位贴镜。这条的时长校准是下篇的故事。
🔧 设计复盘 · 键位模型:为什么一天翻两版还值得写?因为两版失败的原因不同层。第一版死于设计者视角与玩家预期的错位,状态机的对称性不是产品逻辑;第二版死于隐藏维度,玩家设置这种全局状态让手感不可复现,而不可复现的手感根本无法迭代。终版的三条规矩,每一条都来自一次真实的失败。教训浓缩成一句:键位模型没拍板之前,别急着写退出逻辑——依附于模型的代码,模型一旦推翻就全部作废。附带一个自动化的教训:程序化改变瞄准状态必须走内部状态机直达接口,合成按键事件在不同玩家设置下语义漂移,是自动化管线里的定时炸弹。
五、过渡不是瞬切:定时混合器
三段机位讲清了”有哪些状态”,但状态之间怎么切,是另一件事——而且是弹簧臂层这次改造里技术含量最高的一块。它介于空间和时间之间:混合什么(构图值)属于空间维度,混合多久属于时间维度,所以放在这里承上启下,具体秒数留给下篇。
最初的实现是瞬切:举枪的瞬间,肩射的构图叠加值直接生效。进游戏一看过于生硬,画面瞬间跳到新机位,像剪辑而不是运镜。真实的运镜是有过程的:相机从腰射机位平顺地推到肩射机位,用零点几秒。
于是有了一个定时混合器,把瞄准层的叠加值从”当前值”平顺推到”目标值”而不是替换。听起来简单,做对却有三个坑,每个都是实测逼出来的:
其一,续接而非堆叠。玩家连续快速点按右键(举枪—放下—举枪)是常态。如果每次按键都启动一个新的”从零到满”的混合,多次按键的混合会叠在一起,画面抖动。正确做法是每次目标变化时,从当前的中间值续接:推到一半又松手,就从这一半平顺退回去,不重来、不叠加。混合器内部记录的是”此刻的实际值和期望值差多少”,每帧对比、变则续接。
其二,三种过渡三种时长。进肩射、出肩射、以及肩射中姿态组切换(站→蹲),是三种不同的过渡,快慢本该不同。混合器按”从哪来、到哪去”选对应时长(进、出、重定向三档)。这三个秒数是多少、怎么从参照录像里数出来——是下篇的正题,这里只交代结构上留了三个独立旋钮。
其三,收敛后要钉死。指数趋近的老问题:混合逼近目标时无限接近但永不到达,得在足够接近时直接吸附到目标值。否则会留下一个微小的持续漂移,静止画面里看着像相机在轻轻呼吸。
🔧 设计复盘 · 为什么不复用现成的补间:工程里本来有一套补间系统,但它是为界面动画设计的(面向对象、走委托回调),挂在每帧核心相机路径上太重。混合器最终用的是引擎标准的 alpha 混合基础件——“复用现成的”和”用对工具”是两件事,前者省事,后者省的是运行时开销。这个”不复用”的判断本身也是一次考古:把工程里几套候选(补间系统、界面动画、引擎基础件)都翻出来比过,才敢下结论。AI 擅长的正是这种穷举式比对——把所有能用的都列全,人来判断哪个对。
这块混合器还有一个后续价值,在 FOV 那一节回收:它不只混合位置,后来的视场角偏移也挂进了同一条混合时间线:同一个 alpha、同一套续接逻辑,位置和视场角逐帧同步。一个设计对的通道,会长出计划外的复用。
六、先造尺子:调参面板与数据链路
正式调数值之前,我们先花了两批工作量造了两把尺子。事后看,这是整个机位与构图工作里回报率最高的投资。
第一把尺子:运行时调参面板。纯代码构建、零美术资产的一块调试面板,挂在游戏里实时显示当前机位参数,提供肩射九宫格(三姿态 × 臂长/肩偏/抬高)、基线行、过渡时长旋钮和一键导出。
这块面板自己就是一个小型的”AI 原生开发”样本。工程里现成的调试面板范本翻出来一看,是美术资产加代码绑定的做法:每块面板都要配一个 UI 资产,改布局要开编辑器。但继续考古发现工程里还有一个纯代码构建界面的先例,整块面板全部用代码拼装,零资产依赖。我们照后者做了,好处立竿见影:面板布局的每一次调整(后来加列、加锁、加读数改了七八轮)都是纯代码提交,不碰任何美术资产,版本管理干干净净。
面板的几个细节设计都是被实际痛点逼出来的:基线行跟随移动状态:你蹲下去,面板自动切到蹲姿基线那一行,不用手动选(第一版要手选,调蹲姿时改了站姿的值,一轮作废);按住预览锁:按住按钮时把瞄准层钉在肩射档,松开恢复,专治”参数要在举枪状态下看效果,但右手要按住右键、左手要拨面板,手不够用”;导出到日志:一键把当前全部参数吐成可以直接粘贴的格式,消灭手抄。
为什么不用引擎自带的属性面板?因为迭代回路长度完全不同:属性面板改一个值要停游戏、找组件、改值、再进游戏,一轮分钟级;调参面板是在举枪状态下边看边拨,一轮秒级。机位与构图后面所有的数字收敛,都是在这块面板上完成最后一英里的。

第二把尺子:数据表链路。构图参数进了一张每武器一行的 CSV 表:三姿态的肩射叠加九列、分姿态安全钳三列、过渡时长若干列(后来 FOV 也进来了)。配套一条工作流:CSV 是配置层的唯一权威源,改完一条命令无头导入引擎资产,再一条命令冷读回验证——编辑器全程不用开。
这条链路其实是一个更大工程的局部。为了让”CSV 权威”这件事在全项目成立,我们把工程里一千三百多张数据表全部做了一次导出基线:以后任何一张表的资产和 CSV 出现漂移,一次对比就能发现是谁动了它。这个基线工程还顺带清点出了十几张损坏的表和几张结构上无法序列化的表——都是工程里无人知晓的暗伤。
读取链路做了三级兜底:武器行 → 默认行 → 代码缺省,表坏了游戏也不崩。应用时机上选了”拉模型”:不监听换武器事件,而是在每次举枪的入口处检查”这把枪的参数灌过没有”,没灌才查表:同一把武器期间零开销,而且控制台和面板手调的实验值不会被表反复覆盖,换武器才回归表值。表是归宿,手调是实验,这个优先级语义后来在每一轮调参里都被依赖。
这里的粒度是有意选的:当前阶段以武器类别为一行(同一款枪的所有实例共用一行构图)。这够本阶段用,但显然不是终点——配件、姿态组合、瞄具类型都可能需要独立配置,那是这套表结构未来自然细分的维度,加列即可,不用推倒。先用最粗的粒度把链路跑通,是刻意的:粒度越细,调参迭代量越大,而此刻要验证的是”数据驱动这条路走不走得通”,不是”填满所有格子”。
而”武器类别”这个粒度本身还埋着一个差点上线的坑:表的键。武器 ID 看起来是个整数,直接拿来当键,测试全过。但写码时多看了一眼 ID 的构造:它是”类别+实例序号”的拼包,同一把枪每次拾取,实例序号都不同。拿裸 ID 当键,等于每把捡起来的枪都查不到自己的行,全部静默落到默认行。正确的键是拆包后的类别段。这种坑测试测不出来(测试图的枪恰好序号稳定),只有读构造代码才能发现。
缓存记号的生命周期,必须等于它描述的对象。还有一个只在多局游戏间才现形的坑,需要单独拿出来讲,因为它比相机通用得多。”这把枪已经灌过参”的记号,最初是个静态变量——进程级生命周期。第一局游戏一切正常;第二局开始,新建的弹簧臂还是出厂默认值,但那个静态记号还记着”灌过了”,于是跳过查表——同一份表,两局游戏给出两套构图。这个 bug 阴险在它是无声的:不崩、不报错,只是第二局的相机悄悄不对。修法是把记号从”进程级”降到”弹簧臂实例级”,跟着组件一起生灭。抽象出来的规矩:一个缓存记号描述的是谁,它的生命周期就该等于谁:它记的是”这个弹簧臂灌过没有”,就绝不该活得比这个弹簧臂久。任何”状态记号活得比它描述的对象久”的地方,都是一个等着发作的跨实例 bug。

🔧 设计复盘 · 为什么先造尺子:AI 原生开发的一个反直觉之处:AI 写工具的边际成本极低,所以”先造仪器再干活”从奢侈品变成了默认选项。这块面板和这条 CSV 链路加起来大约两个批次的工作量,换来的是后面每一轮数值迭代从”分钟级”降到”秒级”。规模效应在后面的构图闭环里兑现:六个构图格子、每个格子两到四轮收敛,没有这两把尺子,这个迭代量根本跑不完。
七、参照测量:录像给”要什么”,探针给”现在是什么”
尺子造好了,接下来是方法论。一句话版本:
录像给”要什么”,探针给”现在是什么”,两者之差就是要改的量。
参照侧,我们按清单录了十四段标杆作的实机素材。清单不是”多录点总没错”,每段在录制前就写好了三样东西:动作脚本(例如”原地举枪收枪五次、开镜离镜五次、腰射直接开镜三次,动作干脆”)、要回答的问题(这段是给过渡时长发出生证的)、判读产出物(数帧得到三个时长参数)。带着问题录素材和”先录了再说”的差别,会在判读阶段被十倍放大——前者每段素材都有明确的消费路径,后者积攒一堆”可能有用”的录像最后谁也不看。十四段的分工:三姿态各一段三态停留(构图)、过渡时长专段、跑停(跟随延迟)、转镜(旋转延迟)、冲刺(视场角)、翻滚、开火受击(震屏)、战备呼叫、换肩、多武器对比。素材统一抽帧成 385 张带网格和时间码的判读帧。
录完之后有一个反直觉的收获:判读结论里近半是”不用做”。翻滚段的定论是”镜头稳定跟位移即可,无需专门运镜”;战备呼叫段是”无特殊相机行为,悬案解除”;转镜段印证了”旋转零延迟”。三段素材消灭了三项想象中的工作量——测量不仅告诉你差多少,也告诉你哪里不差,后者砍掉的需求都是纯利润。
我们侧,探针是分批长出来的:调试面板的实时读数、状态机文本、后来还有专门的采样器把每帧相机数据落成 CSV。原则是先证明链路健康,再谈数值对比。这条原则的两次救场故事在下篇 lag 一节。
网格判读是最朴素也最可靠的量化手段:在参照帧和我们的截图上叠同规格的网格,读角色头盔的横坐标、宽度、和地平线的相对位置。三个读数就把”构图”钉死了:横坐标对应肩偏、宽度对应臂长、地平线关系对应俯仰,一个读数锚一个参数,不多不少。深奥的图像技术后面有(下篇的判读工具箱进化史),但机位与构图判读的主力就是这三个网格读数。
八、构图闭环:从坡地污染到公式反解
机位与构图对齐的重头戏:把三姿态 × 腰射/肩射共六个构图格子逐一对齐参照。这一节完整讲站姿肩射这一格,因为它把这套方法的坑和解全走了一遍。
第一轮:绝对靶,失败。从参照录像读出目标:”头盔中心在横向 43%、宽度占屏 6.6%、头盔在地平线下方 17%”。前两个读数顺利收敛,第三个把我们引向了错误的方向:照着”地平线下 17%”调出来的机位,臂长比原值长出近一倍,相机高高吊在角色脑后。进游戏一看:相机被抬到了明显不合理的高度。
失败的成因有必要写清楚。参照录像里角色站在坡地上,前方地形下坠——录像里”头盔在地平线下 17%”这个读数,大半是地形贡献的假信号。而我们的测试图是纯平地。拿参照作在坡地上的绝对构图直接套到平地上,等于把地形差异全都算进了相机参数。这一轮是用户两次目检拦下来的:”太远了””太高了”——数字上”收敛落带”的方案,眼睛一票否决。
第二轮:相对差值靶。修正思路:同一段录像里,腰射段和肩射段的地形、俯仰是同一套,取”肩射减腰射”的差值做靶,地形污染自相抵消。绝对量只保留头盔宽度一项(物体尺寸不受地形影响)。新靶:宽度 6.6%、横向相对腰射偏移 −1.3%、相对腰射下沉 +5.2%。
第三轮:公式反解。有了干净的靶,下一个问题是怎么从”屏幕读数”反推”引擎参数”。手动调面板逐步逼近可行但慢,而且收敛之后说不清”为什么是这个数”。我们为三个读数各建了一个一阶投影近似模型:不是严格的透视投影(那要连同视场角、宽高比、相机旋转一起解),而是在”小角度、固定视场角、目标尺寸近似固定”的工作区间里,把这些因素当常数吸收掉,只留最主导的那一项关系:
- 头盔宽度 w ≈ C_w / d(d 为相机到头的距离)
- 横向位置 x ≈ 50 − 50·SY / d(SY 为肩偏)
- 地平线关系 bh ≈ K·(TZ − h₀) / d(TZ 为观察点抬高)
三个常数 C_w、K、h₀ 不从图纸来,从两张实测截图标定:用面板设置两组已知参数并截图读数,解出常数,近似模型就锚在了我们自己引擎的这个工作区间上。之后反解目标读数得到参数组,两轮微调落带:终测宽度 6.3%(靶 6.6,读数噪声内)、横向 43.4%(靶 42.9)、相对下沉 +6.1%(靶 +5.2)。近似模型只负责”把盲目试探变成两次有方向的调整”,最后落带与否仍由实测截图判定。
定稿值是一组臂长/肩偏/抬高的叠加量,而改造前代码里遗留的那组推导值,对照近似模型一算方向都错了两处:推近过了头,横移拧反了方向。它是更早的时候”看着差不多”手写的,从没被工具链验证过。这就是本文论点里”测量层”的价值:不量化,错误的参数可以在工程里潜伏很久。



六个格子里还有两个惊喜是”零改动”:蹲姿和站姿的腰射基线,实测构图和参照对齐后发现原值就是对的,一格没动。测量既能找出错的,也能证明对的,后者省下的重构是隐形收益。
另外四格各有各的故事,捡三条有代表性的:
蹲姿肩射:一格干净的标准流程。这一格没有出现失败,写它恰恰是因为它展示了方法成熟后的样子。参照录像里锁定一帧干净的蹲姿肩射停留帧做靶:头盔横向 37%、宽度 9.8%、地平线下 7.5%。面板拨值、截图、网格读数、对照、微调,两轮。终测 35.3%、9.4%、+4.4%,全部落在读数噪声带内,即告完成。全程不到一小时,其中大半时间花在进出游戏截图上。对比第一轮站姿(坡地污染那次)用了整整一晚上仍以失败告终——同一套方法,靶干净与否决定了十倍的效率差。这也是为什么值得把”选靶”本身当成一道工序:判读之前先判素材。
蹲姿的钳位巧合。蹲姿肩射定稿臂长恰好落在安全钳边上(65 对 65)——合成臂长贴着钳位运行。这不是失误,是几何巧合:蹲姿本来就该贴近。但它提醒了一件事:如果哪天改了叠加值发现”怎么调都不动”,先看是不是坐在钳位上:保险丝烧了不会响,被钳住的参数也不会喊。调参系统里每一个”静默生效”的保护机制,都值得配一个可见的提示。
趴姿是唯一动基线的格子,而且是两个参数联动的教训。第一版只调臂长,调到目标距离后进游戏一看:相机贴着角色头顶俯拍,像监控探头。原因是趴姿的观察点还挂在站姿的高度上:角色躺平了,”眼睛”没跟着下来,相机围着一个悬空点转,怎么调距离都是俯视。臂长和观察点高度两维共同决定构图,只动一维等于在错误的圆轨道上找正确的点。趴姿基线最终三个参数全动,配合参照录像里”贴地平视、地平线压在画面中偏上”的读数才落带。
安全钳从一个数变成三个数。肩射叠加是负值(推近),任何配置失误都可能把臂长减成负数、相机缩进角色身体里。第一版安全钳是单一常量:臂长不低于 80。跑一轮实测发现它反而制造了问题:趴姿的合成臂长被钳在 80 以上,比蹲姿还远,与参照作”姿态越低相机越贴身”的规律正好相反。原因是安全距离取决于相机与角色的相对几何:站姿角色是竖直柱体,相机要绕过头肩,需要大间距;趴姿角色躺平、相机在身后平视,穿模风险反而最小。单一钳值等于用最保守的姿态约束全部姿态。定稿是分姿态三个钳值(80/65/50),并且立了一条使用哲学:钳位值取略小于目标臂长,因为安全钳的职责是”守护”(参数配错时兜底),不是”塑形”(不参与正常构图)。守护逻辑一旦开始塑形,调参就变成了和自己的保险丝搏斗。
全程六格,每格两到四轮,全部落带。
九、FOV:一次简并判读
构图闭环后还剩一项残差:我们的有效视场角比参照宽了一截,广角感明显。直觉方案是”肩射时收窄视场角”——但先得回答一个问题:参照作举枪时到底收不收视场角?
这个问题比看上去难。屏幕上一个物体变大,可能是相机推近了(推轨),也可能是视场角收窄了(变焦)——单个物体的尺寸变化,不足以区分推轨和变焦:只盯着一个目标,它变大 2.9 倍这件事,两种成因能给出完全一样的画面。参照录像里腰射到肩射头盔正好放大 2.9 倍,这 2.9 倍里推轨和变焦各占多少,光看头盔无解。
解法是双深度判读:相机平移对远景(几十米外的地标)的角尺寸几乎没有影响,但变焦对全画面等比生效。远景变没变,就是变焦有没有发生的判据——因为远景对相机前后移动免疫,它身上任何等比的缩放都只可能来自视场角变化,正好把推轨和变焦这对纠缠量分开了。于是我们对录像里的远景天际线做拟合:把腰射帧和肩射帧的整条远景剪影线,套进一个”以光心为中心、只做缩放加平移”的模型,网格搜索让两帧最吻合的那个缩放系数。三对帧独立拟合,系数收敛在 1.07~1.10——参照作举枪时确实收窄了视场角,约 5 度。
这里还有一个”抄绝对值还是抄差值”的方法论选择。参照作的基础视场角和我们不同(机位体系整体不可比),直接抄它的绝对度数没有意义;但”举枪比腰射收窄多少”这个差值是跨工程可迁移的——它编码的是”瞄准=聚焦”这个镜头语言的强度,而不是某个具体镜头的参数。机位与构图里凡是跨工程搬数字的地方都遵循这条:绝对值锚在自己工程的实测上,差值才向参照看齐。构图的相对差值靶、这里的视场角差值,都是同一个原则的实例。

工程侧随即落了一条 FOV 通道。设计原意很简单:视场角偏移跟着”举枪程度”渐入渐出,跟机位推近同步。第一版实现用了现成的”混合进度”接口:乘上偏移值即可。落码前复核那个接口的语义,发现一个严重隐患:它返回的是混合的完成进度,混合结束后恒为 1——也就是说,站着不动不举枪时它也是 1,直接乘会让腰射常态也应用全额收窄。”进度”和”程度”一字之差,语义完全不同。正确做法是把 FOV 做成瞄准层值混合器内部的平行标量:和机位偏移共用同一条混合时间线、同一个中断续接逻辑,端点在”零”和”配置值”之间:腰射时干净归零,任何过渡状态下和机位推近逐帧同步。
然后是一个产品决策:通道落地,默认关闭。判读证明了参照这么做,但”我们要不要跟”是另一个问题:拍板是机位先行,视场角保持全程稳定,想开的话是表里填一个数的事。功能与数据分离的模式在这里第一次显出它的决策价值:机制的落地和口味的启用,可以是两次独立的拍板。判读数字(收窄约 5 度)作为参考值写进了表结构的注释里,哪天决定启用,不用重新考古。
十、换肩:写完即封存的取舍
机位与构图的最后一格是换肩。这一格的开场是一次判读裁决:换肩在原始需求里被排到了后置,因为”参照作有没有换肩”一直是悬案。专门录的那段素材给了实证:角色在画面右侧 62% 处举枪,和常态的左侧 40% 正好镜像,参照作有换肩,值得做。
设计过程有两个不显然的决定。其一,镜像的作用点必须在全部构图层合成之后。相机的横向偏移不止肩偏一个来源:基线、室内收臂层、瞄准叠加、视口比例曲线都在往 Y 通道里写数。如果只翻肩偏一个参数,腰射时看着换了肩,一举枪瞄准叠加又把相机拉回原侧,构图来回跳动。正确做法是所有层合成完毕后、在最终消费点对横向通道整体乘以正负一——换肩是构图级的镜像,不是参数级的。其二,准星永远居中:换肩只动相机不动弹道,玩家的肌肉记忆零成本迁移。
键位是个小插曲:想给换肩找个顺手键,把候选键翻了一遍——全被占了。最后落在鼠标前侧键上,还得给键位表加行。这种”功能十分钟、键位半小时”的体验,做过输入系统的应该都会心一笑。
代码写完了。然后封存了。
封存的判断依据:换肩服务的是掩体交战体验(贴左掩体探头时换到左肩,少露半个身子),而工程当下连敌人都还没有。它的优先级排不到基础手感收尾之前。于是代码全量进了一个特性分支,主干零残留,新加的键位行从提交列表里撤了回来,设计和键位调研全部入档。哪天掩体玩法立起来,切回分支继续开发即可。
这也是这个系列想如实记录的一种日常:不是所有写完的代码都应该合入。判断”现在要不要”和判断”怎么做对”同样重要,前者砍掉的是维护成本的期货。
十一、机位与构图的验收线
上篇的交付物汇总一处,也算给”改造完成”下一个可检验的定义。机位与构图的验收线是:三姿态 × 腰射/肩射共六格构图,每格三项读数(横向/宽度/地平线关系)全部落在参照靶的读数噪声带内,且每个数字能说出出处。
| 格子 | 基线(臂长/肩偏/抬高) | 肩射叠加 | 备注 |
|—|—|—|—|
| 站姿 | 原值零改动 | −121 / −18 / +14 | 老推导值方向错两处,公式反解两轮落带 |
| 蹲姿 | 原值零改动 | −85 / −10 / −5 | 标准流程两轮,臂长贴分姿态钳位 |
| 趴姿 | 三参数全动 | −140 / +20 / −20 | 唯一动基线的格子,臂长×抬高联动教训 |
外加:分姿态安全钳(80/65/50,守护不塑形)、FOV 通道(判读值 −5° 入注释,默认关)、换肩(设计完整,分支封存)。每一行都能回溯到具体的参照帧和判读记录。这句话在五月是做不到的,现在是工作流的副产品。
十二、AI 协作复盘(上)
按系列惯例,固定四问。
AI 帮了什么?这一篇范围内:调参面板和 CSV 链路的全部代码(范本考古、纯代码先例的发现和照搬也是它做的);385 张判读帧的抽帧、网格叠加与量化读数;公式反解的建模、常数标定和求解;六个构图格子每一轮的数值计算与落盘:CSV 改值、无头导入、冷读回验证的全自动执行,人只在 PIE 里看结果;全项目一千三百多张表的导出基线工程;以及贯穿全程的档案:每批工作即做即写进共享档案,任何一个会话中断后,下一个会话一句话就能从断点完整接续。
AI 在哪里出了错?三次有代表性的。其一,绝对构图靶被坡地污染,AI 按数字”收敛落带”毫无察觉,把相机抬到了明显不合理的高度——AI 会把错误的参考系执行得非常精确,参考系层面的错误它自己发现不了,是人的眼睛两次否决拦下的。其二,调参面板的数字框做窄了,负号被截掉,”15.0″实际是”−15.0″,AI 读面板截图记档时差点把负值当正值定稿,最后是靠构图物理关系反推识破的(这个参数为正时头盔应该上移,画面里明明在下移)。其三,一次读表时按行名匹配武器 ID 落空,差点当场断言”这把枪没有配置”,实际那张表用另一个字段做键,行名只是备注。三次出错共享同一个形状:执行层无可挑剔,参考系/语义层出错,而且错得很自信。
人怎么补位?三类不可替代的动作。目检否决:数字说”落带”、眼睛说”不对”时,眼睛赢——两次都赢对了。口味拍板:FOV 关、换肩停、每格构图的最终”顺眼”确认、以及”机位先行”这类范围决策,全部是人的判断。物理在环:所有编译和进游戏验证都由人执行,AI 的每一批改动都强制停在”等待实测”的检查点上。这个节奏是质量机制:本篇里三次出错有两次正是在这里被拦下的。
最终怎么解决?每次出错沉淀一条可复用的规矩:绝对靶换相对差值靶(地形消元);读数存疑时用物理关系交叉验证,仪器显示不可尽信;查表先查键的定义再下结论。这些规矩连同全部定稿数值、判读方法、失败尝试,都进了长期档案。这是”AI 原生”里复利最高的一环:下一条产线开工时,这些教训是免费的——它们不随会话结束而蒸发,也不依赖某个人的记忆。
*下篇预告:相机的时间维度(过渡与插值):过渡时长的逐帧审计、ADS 双向硬切背后的基座层漏网、移动跟随延迟的”结构性死亡”与复活、开火反馈的四层配置链路,以及把这一切串起来的判读工具箱进化史。*