PVE 玩法客户端架构:从进局、新手到结算恢复
这篇把 PVE 玩法放进一次完整对局生命周期中回答。团队系统会尽可能覆盖,但每个模块都标明个人直接交付、项目协作或源码复盘,不能把“现在会讲”偷换成“当时全由我做”。
面试官:先介绍一下你的玩法经历
30 秒回答
我先后做过非对称竞技、PVP 和 PVE。直接投入较深的是局内 HUD、物品道具与交互、Gameplay Ability 接入,以及 PVE 新手流程、任务配置、局内外时间线和线上问题。完整 PVE 架构包括局外队伍匹配、加载与重连,局内阶段、地牢、AI、技能、交互、物品和撤离结算,再把局内临时收益幂等提交到局外。地牢生成、完整怪物 AI 和底层战斗框架不是我独立负责的,但它们与我的流程有大量接口,我已经结合项目源码补齐了系统理解。
90 秒回答
我会从一局游戏讲。局外先确定队伍、模式和装备,匹配服务返回关卡实例票据;客户端按依赖清单加载资源并进入 DS。服务端 GameMode 推进准备、探索、事件、Boss、撤离和结算,GameState 把公共阶段和权威时间同步给客户端。角色侧由属性、Ability、Effect、动画和相机组成;世界侧由地牢、房间、门、宝箱、机关、怪物和导航组成;UI 只消费这些领域状态。
客户端最难的不是功能数量,而是三条边界:输入意图与服务端权威结果分开,局内临时数据与局外永久资产分开,实时增量与重连快照分开。我直接做的新手和时间线看似是流程功能,实际贯穿 AI、导航、镜头、任务、UI、服务端时间和恢复;物品与交互则让我接触了实例身份、事务、Ability 和复制。完整地牢和 AI 属于团队系统,我按架构回答,追问 ownership 时会立即收回个人边界。
面试官:画一下 PVE 客户端架构
flowchart TB
SESSION["会话层<br/>队伍 / 匹配 / 加载 / 重连 / 切图"]
RULE["规则层<br/>GameMode / GameState / 阶段 / 目标 / 结算"]
GAMEPLAY["Gameplay 层<br/>角色 / Ability / 武器 / Buff / 交互 / 物品"]
WORLD["世界层<br/>地牢 / 房间 / 机关 / AI / 导航 / 生成"]
VIEW["表现层<br/>HUD / 引导 / 小地图 / 相机 / 动画"]
DATA["数据层<br/>配置 / 任务 / 库存 / 奖励 / 埋点"]
INFRA["基础设施<br/>复制 / RPC / 资源 / 对象池 / 日志 / 自动化"]
SESSION --> RULE
RULE --> GAMEPLAY
RULE --> WORLD
GAMEPLAY --> VIEW
WORLD --> VIEW
DATA --> SESSION
DATA --> RULE
INFRA --> SESSION
INFRA --> GAMEPLAY
INFRA --> WORLD记忆顺序:怎么进局 -> 谁推进规则 -> 玩家和世界怎样交互 -> UI 怎样投影 -> 结果怎样回到局外。
面试官:一局 PVE 的状态机怎样设计?
Lobby
-> Matchmaking
-> AllocatingInstance
-> Loading
-> Preparing
-> Playing
-> Escaping / Failed
-> Settling
-> Returning
-> Lobby服务端拥有局内阶段、阶段序号和权威时间。客户端收到新阶段后执行幂等的 Exit(old) 与 Enter(new),旧序号通知直接丢弃。UI 动画播完不是状态迁移依据;断线重连也不重放全部历史,而是先安装当前快照,再补尚未完成的必要表现。
每个状态退出时必须取消加载、timer、输入模式、UI 订阅和临时相机。很多“第二局才出问题”的根因,就是第一局退出只隐藏了页面,没有释放状态拥有的资源。
面试官:局外到局内怎样衔接?
局外确定队伍身份、玩法参数和入局装备快照,匹配返回实例票据;客户端进入加载态后按资源组预热,连接 DS,再用票据恢复玩家与队伍身份。服务端确认玩家进入后下发当前规则快照,客户端才打开局内输入和 HUD。
失败要按阶段处理:匹配可取消,实例分配超时要回局外,资源缺失要报告具体依赖,连接失败可以重试票据,进入 DS 后则走重连而不是重新匹配。不能让多个异步回调都拥有“打开局内页面”的权力。
面试官:新手引导为什么不是一串弹窗?
直接回答
新手关是战斗、AI、导航、相机、任务和 UI 的受控演练,本质是一个跨系统、可恢复的步骤状态机。任一异步事件丢失,都可能让玩家永久卡在某一步。
步骤契约
GuideStep
- enterCondition 是否允许进入
- completionCondition 当前世界是否已经完成
- presentation 高亮、遮罩、对话、镜头、引导线
- subscriptions 监听哪些领域事件
- timeoutPolicy 等待多久、怎样兜底
- skipPolicy 是否允许跳过,跳过提交什么里程碑
- recoveryCheck 重进后如何从事实源恢复
- diagnostics step / reason / result进入时注册资源,退出时统一解绑。步骤推进应优先检查“完成条件现在是否成立”,而不是依赖某个只发一次的回调。关键里程碑由服务端或持久层保存;高亮和引导线只是表现,不是进度真值。
面试官:你做新手流程时遇到过什么复杂问题?
STAR:地图完成和对话完成的竞态
现象: 新手关偶现没有进入正常开局阶段,进一步表现为室内引导线缺失或流程卡住。它不是稳定复现,单看 UI 没有明确报错。
排查: 我把流程拆成地图生成、导航构建、开场对话、状态切换和引导线请求几个时间点。原逻辑分别在“地图完成”和“对话完成”回调中尝试切状态,还额外要求多个状态值同时一致。不同机型和加载速度下,两个事件顺序不固定;条件第一次不满足后又可能不再得到第二次机会。
处理: 把两个入口合并到同一个 TryStartPlay,显式检查“地图已完成 + 对话已完成”,增加只触发一次的幂等标记;条件未满足时保留受控重试,并在真正切换前延迟一帧,让同一地图完成事件上的导航和楼层初始化先收口。同时补了导航构建、楼层数据和过期请求日志,避免只在引导页面打点。
flowchart LR
MAP["地图生成完成"] --> TRY["TryStartPlay"]
DIALOG["开场对话完成"] --> TRY
TRY --> CHECK{"两个条件都满足?"}
CHECK -->|否| RETRY["受控重试 / 保留诊断"]
CHECK -->|是且未触发| NEXT["延迟一帧进入 StartPlay"]
NEXT --> NAV["引导与导航读取已完成世界"]反思: “等两个事件”不能写成两个回调里各自碰运气。多异步前置条件应由单一协调者聚合,完成条件可重复计算,推进动作幂等。更理想的做法是给每个前置条件和状态切换建立自动化故障注入,随机交换完成顺序。
这个案例能证明排查过程和机制,但没有留存统一故障率前后对比,所以不报量化收益。
面试官:技能系统怎样设计?
一次 Gameplay Ability 至少包含:
输入意图
-> CanActivate:标签、资源、目标和状态检查
-> Activate
-> Commit:扣资源、进入冷却或锁定成本
-> AbilityTask:动画、等待、目标选择、位移、命中
-> ApplyEffect / GameplayCue
-> End 或 Cancel
-> 解绑回调并恢复输入、动画和临时状态客户端可以预测动画、按钮反馈和部分移动,但伤害、消耗、掉落和公共状态由服务端权威。服务端用预测键或执行身份确认结果,客户端收到拒绝时回滚表现。
最常见问题不是技能“放不出来”,而是取消路径不完整:Montage 被打断,Task 仍监听;角色死亡,目标选择还占用输入;重触发创建新实例,旧计时器继续回调。排查时先画生命周期,再查标签和网络角色。
面试官:目标选择和服务端校验怎样分工?
客户端根据镜头、距离、角度和碰撞快速生成预览目标,让玩家有即时反馈;提交时发送目标身份和必要参数,服务端重新检查施法者状态、距离、视线、阵营、目标存活和时间窗。服务端不能信任客户端上传的最终伤害或“目标合法”布尔值。
范围技能还要区分视觉落点和权威落点。客户端地形投影只负责显示,服务端使用自己的世界数据校正;偏差超过阈值时拒绝或修正。
面试官:通用交互物怎么设计?
交互分四步:发现候选、展示提示、提交意图、服务端完成事务。
| 阶段 | 客户端 | 服务端 |
|---|---|---|
| 发现 | 空间查询、排序、选择最佳候选 | 不持续参与每帧 UI 查询 |
| 开始 | 显示按钮或读条,发送对象 ID 和交互类型 | 校验距离、资格、状态、占用 |
| 进行 | 播放进度,可预测取消表现 | 监控移动、受击、对象失效等打断条件 |
| 完成 | 等待结果,更新表现 | 幂等提交奖励、状态或物品转移 |
动画结束不能等同业务成功。多人同时开箱或拾取时,只有服务端事务决定最终归属。失败原因也应结构化,客户端才能区分距离不足、被占用、背包满和对象已失效。
面试官:物品、背包和撤离为什么难?
物品类型 ID 只说明“这是什么”,实例 ID 才说明“玩家拥有哪一个”。带耐久、随机词条或绑定状态的装备不能按类型 ID 合并。拾取、移动、交换、装备、消耗和撤离提交都可能跨世界对象、背包格子与局外库存,必须按服务端事务处理。
客户端可以先播拾取效果,但要保留 pending 状态;服务端返回 事务号 + 新版本 + 变更集 后再确认。若客户端发现版本缺口,直接重拉快照,不继续把局部补丁应用到未知基线。
局内收益只在撤离或结算成功后提交到局外。提交绑定关卡实例和事务号,重复请求返回同一结果,避免断线重试导致重复发奖。
面试官:3C、动画和 Gameplay 怎样解耦?
Gameplay 决定意图和权威状态,例如技能已激活、角色受控、进入观战;相机和动画将状态投影成反馈。Ability 不应直接永久接管某个相机对象,而应请求一个有优先级和生命周期的相机模式;动画通过事件或任务返回完成/中断,不直接修改奖励或伤害真值。
相机模式至少要处理:进入、更新、被更高优先级打断、目标销毁、角色重生和退出恢复。输入焦点也要与 UI、角色和观战模式统一仲裁,否则页面关闭后仍可能无法转镜头或移动。
面试官:怪物 AI 的完整链路是什么?
感知 / 仇恨
-> 黑板或上下文
-> 行为树选择意图
-> EQS 生成、过滤、评分目标位置
-> 导航与移动执行
-> Ability / 攻击执行
-> 动画和客户端表现行为树 Task 必须可中止,目标死亡、玩家离开、寻路失败和控制状态变化都要退出到可恢复状态。EQS 适合回答“哪里更合适”,不是每帧运行的完整 AI;查询要限频和缓存。大量怪物的优化先做感知/决策降频、空间相关性、休眠和对象池,再考虑简化行为。
完整怪物 AI 和地牢框架不是我的直接主责。我的工作会消费它们的状态,例如新手 AI、目标高亮、任务进度和 HUD,因此我能从接口与源码解释,但面试中用“团队系统”作为主语。
面试官:局内系统如何处理死亡、观战和重连?
死亡是权威状态变化,客户端先关闭角色输入和临时 Ability,再进入死亡表现;是否可复活、何时转观战由规则层决定。观战目标可能死亡、离开或未完成复制,因此相机管理器要能选择下一个合法目标,找不到时退回安全视角。
重连不依赖曾经收到的事件,而是按领域安装快照:当前阶段与剩余时间、玩家生死和观战目标、任务与交互状态、背包、世界关键对象。安装快照后,再接收高于该版本的增量。表现事件可以选择补播、跳过或直接投影到终态。
面试官:结算怎样避免重复发奖?
服务端首先以关卡实例锁定结束条件,只允许从未结算进入结算中;计算结果后生成唯一结算事务,永久数据层对事务号幂等。客户端可以重复查询结果,但不能重复触发计算和发奖。
客户端结算页只展示权威结果,并区分“结果已生成”“永久资产已提交”“表现播放完成”。玩家中途断线或跳过动画后,回到局外仍可根据事务号刷新同一结果。
面试官:局外系统有哪些核心状态?
局外不是若干独立页面,而是队伍、匹配资格、装备、仓库、任务、职业和活动时间线的组合。页面只订阅领域模型;服务器时间与配置版本决定活动节点,本地倒计时只做平滑显示。
我直接做过局内外时间线和客户端/服务端联调。关键点是场景切换后重新读取当前节点与权威时间,旧页面回调带节点版本校验;领取和跳转请求有请求号,避免重复点击或重连造成重复处理。
面试官:如何做 PVE 性能治理?
按阶段和系统拆,不先猜结论:
| 维度 | 常见来源 | 方法 |
|---|---|---|
| DS CPU | AI 感知/行为树/EQS、复制、刷怪 | 分项 stat、固定波次、尾帧采样 |
| Client CPU | UI 刷新、Lua Tick、对象创建、动画 | trace、调用采样、对象池前后对照 |
| GPU | 角色/怪物材质、特效、灯光、遮挡 | GPU profile、场景分段、画质档位 |
| 内存 | 关卡资源、UI、动画、对象池未回收 | LLM/内存快照、进退局差分 |
| 网络 | Actor 候选、属性扫描、RPC 峰值 | RepGraph 统计、抓包、丢包测试 |
验收要固定设备、地图种子、玩家脚本、怪物数量和阶段,记录 P50/P95/P99,而不是只看一次平均帧率。高密度复制的具体方案见独立专题。
面试官:你个人做到了哪里?
直接交付
- 非对称玩法的局内 HUD、观战结算、技能和状态表现问题。
- PVP 的物品道具、交互对象、宝箱、Gameplay Ability 与复制接入。
- PVE 的新手流程、镜头步骤、智能提示、局内外时间线、任务/故事线、配置、界面和线上问题。
项目协作或源码复盘
- 队伍匹配、完整地牢生成、完整怪物 AI、底层战斗框架和全量经济系统。
- 我能说明它们如何和自己的模块协作,也能完成架构设计题,但不会说成历史上由我主导。
快速背诵版
我会从一局 PVE 的生命周期讲架构:局外队伍、匹配和装备形成入局快照,客户端加载资源并连接 DS;服务端 GameMode/GameState 推进准备、探索、战斗、撤离和结算;角色侧由 Ability、Effect、物品和交互组成,世界侧由地牢、机关、AI 和导航组成,HUD、新手、小地图和相机只投影领域状态。关键是权威与表现分离、局内与局外资产分离、快照与增量分离。我直接做得较深的是新手、时间线、物品道具、交互和 Ability 接入;地牢和完整 AI 是团队系统。最典型的问题是地图生成与对话完成的异步竞态,我把多个回调合并成幂等的条件协调器,并让导航初始化在状态切换前完成。
责任边界
| 内容 | 面试表述 |
|---|---|
| HUD、物品道具、交互和 Ability 接入 | 可说“我直接负责/实现过” |
| PVE 新手、时间线、任务配置与线上维护 | 可作为个人主案例 |
| 引导线竞态和流程卡死 | 可讲真实 STAR,但不报无依据故障率 |
| 地牢生成、完整 AI 和底层战斗框架 | 说“团队系统和源码复盘” |
| 匹配、经济和所有局内外系统 | 说“参与接口和联调”,不扩大 ownership |