FPS武器系统架构设计 一、先明确架构要解决的核心矛盾在动手设计前先想清楚武器系统真正难的地方在哪。它不是按键开枪这么简单而是要同时满足四组相互冲突的需求手感 vs 公平客户端要即时反馈延迟感致命服务器要权威判定防作弊表现丰富 vs 性能几十把枪各有动画特效但帧预算有限策划可调 vs 程序稳定数值天天改代码不能天天动武器差异化 vs 复用步枪狙击霰弹逻辑各异又不能每把枪重写一遍好的架构本质上是用分层和组合来化解这些矛盾。下面的设计都围绕这四点展开。二、分层把什么时候变作为切分依据我按照变更频率来分层而不是按技术类型。这样改数值不碰逻辑改逻辑不碰网络。数据层 武器配置、数值曲线 ← 策划每天改热更 逻辑层 开火/弹道/伤害/状态 ← 程序偶尔改需回归测试 表现层 动画/特效/音效/镜头 ← 美术音效改与逻辑解耦 网络层 同步/预测/命中回滚 ← 底层稳定几乎不动关键原则逻辑层不能依赖表现层。开火逻辑发出我开火了的事件动画和特效去监听而不是开火函数里直接调用播放动画。这样做测试逻辑时可以完全不加载美术资源。三、武器实体组合优于继承新手容易设计成AK47 : Rifle : Weapon的深继承树结果狙击枪也想要连发、霰弹枪也想开镜时继承树就崩了。大厂普遍用组合一把武器 一个空壳 一组能力模块。publicsealedclassWeapon{publicWeaponConfigConfig{get;}// 数据publicWeaponRuntimeStateState{get;}// 运行时状态// 能力模块按配置装配privateIFireMode_fireMode;// 全自动/半自动/连发privateIBallistics_ballistics;// Hitscan / 实弹privateRecoilModule_recoil;privateSpreadModule_spread;privateAmmoModule_ammo;privateAttachmentSet_attachments;}AK和狙击枪的区别不是两个类而是同一个Weapon装了不同的_fireMode和_ballistics。加一把新枪多数情况只是加一份配置不写代码。四、开火流程一条清晰的主干开火是整个系统的心脏我把它设计成一条职责单一、顺序明确的管线。每一步只做一件事方便定位问题和插入网络校验。1. 输入 → 触发开火意图 2. 门禁 → 射速/弹药/状态是否允许任何一项不过直接返回 3. 方向 → 基础朝向 散布偏移 4. 弹道 → Hitscan 射线 或 生成实弹 5. 命中 → 找到 hitbox 和距离 6. 伤害 → 交给伤害系统结算本地仅预测 7. 事件 → 广播 OnFired表现层接管门禁这步单独抽出来很重要privateboolCanFire(){floatinterval60f/Config.RPM;// 射速换算成间隔if(Time.time_lastFireTimeinterval)returnfalse;if(_ammo.Current0)returnfalse;if(State.IsReloading||State.IsSwitching)returnfalse;returntrue;}射速用 RPM 换算而不是硬编码间隔是因为策划表和配件系统都以 RPM 为单位思考保持数值语言统一。五、弹道两种模型对应两种游戏气质这是需要在项目初期就定死的架构决策因为它影响网络同步方案。Hitscan射线瞬中——适合竞技射击CS、Valorant。开枪瞬间一条射线定生死判定简单、网络好同步、手感干脆。Projectile实弹飞行——适合拟真射击战地、PUBG。子弹有初速、下坠、飞行时间需要打提前量。真实感强但同步复杂。实弹方案有个必须处理的坑子弹一帧飞很远时会穿墙。所以不能用位置点检测要用上一帧到这一帧的线段检测voidTick(floatdt){_velocityGravity*dt;Vector3next_pos_velocity*dt;// 用 Linecast 覆盖整段位移防高速穿透if(Physics.Linecast(_pos,next,outvarhit))OnHit(hit);_posnext;}选哪个不是技术优劣问题而是你想要什么手感。定了它后面的网络方案才能定。六、后坐力与散布把可控和随机分开这是FPS手感的灵魂很多人会混为一谈其实是两套独立系统。后坐力Recoil是可学习的、确定的。AK的弹道图案永远是先上抬再左右摆玩家靠练习可以压枪。所以它是一组预设的偏移序列随开火次数推进publicVector2NextRecoil(intshotIndex){// 预设图案确定性玩家可背板压枪return_pattern[Mathf.Min(shotIndex,_pattern.Length-1)];}散布Spread是不可控的、随机的。它体现这把枪本身有多准受移动、跳跃、开镜、连发影响玩家无法完全消除只能通过蹲下、开镜、点射来降低publicfloatCurrentSpread(){floatsConfig.BaseSpread;if(State.IsMoving)sMovePenalty;if(State.IsAirborne)sJumpPenalty;if(State.IsAiming)s-AimBonus;s_continuousShots*ShotPenalty;// 连发越久越散returnMathf.Clamp(s,MinSpread,MaxSpread);}分开设计的好处调这把枪好不好压改后坐力图案调这把枪准不准改散布参数两者互不干扰策划调起来心里有数。后坐力在表现上还要拆成三层——镜头上抬、准星扩散、枪模后仰它们可以用不同曲线让画面表现和实际弹着点分离这是高级手感调校的空间。七、伤害一条链式结算伤害不是武器伤害 固定值而是一连串乘算。设计成可扩展的链条以后加新机制如爆头特效、护甲穿透直接插节点。floatdamageConfig.BaseDamage;damage*Config.Falloff.Evaluate(distance);// 距离衰减damage*HitboxMultiplier(hit.part);// 部位头x2 身x1 腿x0.75damage*penetrationFactor;// 穿透衰减damageApplyArmor(damage,target.armor);// 护甲命中盒hitbox要按部位拆成独立碰撞体头、胸、腹、四肢各一个挂不同倍率。这是FPS判定精度的基础也是后面网络回滚要还原的对象。八、网络同步整个系统最难的部分前面所有逻辑在单机都能跑联网才是大厂和小作坊的分水岭。核心原则一句话客户端负责好看服务器负责算数。服务器权威Server Authoritative客户端说我开枪了、我好像打中了最终是否命中、扣多少血全由服务器决定。客户端上报的一律当请求而非结果否则就是外挂天堂。三个必备技术客户端预测本地立刻播开火动画、出枪口火焰不等服务器回包。否则高延迟下开枪像卡了半拍。延迟补偿 / 命中回滚Lag Compensation这是最精妙的一环。你屏幕上看到敌人在A点开的枪但你的包到服务器时敌人已经跑到B点。服务器必须把敌人倒带回你开枪那一刻的位置再判定boolValidateHit(Playershooter,Playertarget,HitRequestreq){// 1. 把目标回滚到射击者当时看到的时刻varrewoundtarget.GetHitboxSnapshot(req.ClientTimestamp);// 2. 用服务器数据重算射线是否真的命中该 hitboxif(!Raycast(shooter.EyePos,req.Direction,rewound,outvarhit))returnfalse;// 3. 校验合法性射速对不对伤害数值有没有被篡改if(!ValidateFireRate(shooter)||!ValidateDamage(req))returnfalse;returntrue;}服务器要为每个玩家保存最近几百毫秒的位置快照才能倒带。这是内存换公平。反作弊校验服务器独立验证射速、弹药、伤害数值绝不信任客户端上报的数字。Hitscan 和 Projectile 在这里差异很大——Hitscan 只需回滚一个瞬间Projectile 要在服务器重演整段弹道飞行这也是为什么第五节说选弹道模型决定网络复杂度。九、数据驱动让策划自己调逻辑写死数值是大忌。所有可调参数进配置Unity 用 ScriptableObject或外部 Excel/JSON 表程序只读不写。[CreateAssetMenu(menuNameWeapon/Config)]publicclassWeaponConfig:ScriptableObject{publicstringId;publicWeaponTypeType;publicfloatBaseDamage;publicfloatRPM;publicintMagazineCapacity;publicfloatReloadTime;publicAnimationCurveFalloff;// 伤害距离衰减曲线publicVector2[]RecoilPattern;// 后坐力图案publicfloatBaseSpread;}配件系统在此之上做属性修正聚合行业通用公式是加法和乘法分开算避免多个百分比叠加时数值爆炸最终值 (基础值 Σ固定加成) × (1 Σ百分比加成)配件枪口、握把、弹匣、瞄具各自声明修正项装配时聚合卸下时还原武器逻辑完全不感知配件的存在。十、性能FPS 帧预算很紧对象池子弹、弹壳、弹孔贴花、命中特效全部池化开火时绝不 new/Destroy否则 GC 卡顿会毁掉手感弹孔上限 LRU 淘汰贴花无限生成会爆显存设上限超了淘汰最老的表现 LOD远处队友的武器动画、抛壳、枪口特效降级或跳过碰撞分层射线检测只查该查的层别让子弹去和捡取物、触发器求交网络 AOI只同步视野内玩家的武器状态远处的不发包十一、模块全景WeaponManager 切枪/持有/输入分发 │ Weapon ──── WeaponConfig数据/热更 │ ┌─────────┬───────┼────────┬─────────┐ FireMode Ballistics Recoil Spread Ammo Attachment │ ▼ DamageSystem ──────► NetworkSync权威判定/回滚 │ 客户端预测 ┘ └ 反作弊校验设计思路小结整套架构的核心就三句话按变更频率分层用组合而非继承装配武器让服务器掌握判定权。其余的后坐力、散布、伤害、配件都是在这个骨架上填充的可替换模块。