尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

网易游戏客户端笔试核心考点:C++、算法与网络同步解析

网易游戏客户端笔试核心考点:C++、算法与网络同步解析 1. 先说结论这份卷子到底在筛什么人做游戏客户端方向的人几乎都绕不开网易的笔试。2018 年这套客户端开发工程师BJ笔试卷我到现在还记得几个印象很深的点它不跟你玩虚的第一页就是 C 概念题和代码阅读题紧接着是算法手写最后再来两道游戏场景相关的简答。很多人以为游戏客户端笔试会考引擎 API、Shader 写法、Unity 操作流程拿到卷子才发现完全不是这么回事。这套卷子的核心逻辑可以概括成三句话算法题是保底分拿来拉差距的不是难题本身而是边界条件和复杂度推导C 是硬门槛概念正确只是及格线能读代码、能纠错才是加分项游戏数学和网络同步是区分度所在没有游戏开发经验的人在这里基本藏不住。如果你是准备校招或者社招转行游戏客户端我建议把这份卷子当成一面镜子先照一照自己的知识结构有没有偏。下面我从几个主要板块展开讲每个板块都会列出典型的题型、解题思路、以及我实际遇到过或听说过的翻车点。2. C 与内存管理最容易暴露真实水平的地方2.1 虚函数、虚析构和多态一道题能问出三层理解网易这套卷子里C 部分是除了算法之外占比最重的。它很少直接问“虚函数是什么”这种背诵题而是给你一段有继承关系的代码让你写出输出结果再判断有没有内存泄漏。考察点通常包括基类指针指向派生类对象时析构函数不声明为 virtual 会导致什么后果构造函数和析构函数内部能不能调用虚函数调用了会走哪一层纯虚函数、抽象类、接口类在实际引擎代码里怎么体现。我记得当时有一道题大概是定义一个 Base 类和一个 Derived : public Base 类Base 的析构函数非虚main 函数里Base* p new Derived(); delete p;问会怎样。答案是“派生类析构函数不会被调用可能造成资源泄漏”。这题本身不难但它后面跟着一个小追问如果 Derived 里有一个std::vectorint指针成员泄漏会发生在哪一步这就把“虚析构”从概念题变成了内存分析题。这里分享一下我的作答技巧遇到这类题不要只写结果要把推导过程也写出来。比如“Base 的析构函数非虚delete 基类指针时按静态类型调用析构Derived 的析构函数不会执行内部成员的内存不会释放”。阅卷人一眼就能看出你是背过答案还是真的理解。另外我见过很多人在这道题上栽跟头构造函数里调用虚函数。你可能知道“构造函数里调用虚函数不会发生多态”但原因是什么因为构造派生类对象时先执行基类构造函数此时派生类部分还没有初始化虚函数表指针还指向基类的虚表所以虚调用会落到基类版本。这个机制在引擎代码里也有实际映射不要在组件基类的构造函数里调用一个期望被子类重写的 Init 方法很多 bug 就是这样埋下的。2.2 内存对齐一字节一字节算给你看内存对齐几乎是网易笔试的保留项目。它考察的是你对结构体内存布局的理解不是单纯背“对齐规则”就完事。我记得一个很典型的题目struct A { char a; int b; short c; };问这个结构体在 64 位默认对齐下的大小。很多人脱口而出 8 字节1427 凑整实际答案是 12 字节。原因很简单a占 1 字节然后为了对齐int4 字节编译器会在a后面填充 3 字节b占偏移量 4 到 7c占偏移量 8 到 9整个结构体的对齐值是 4但结构体总大小必须是最大成员对齐值的整数倍10 不是 4 的倍数所以再填充 2 字节得到 12。如果把成员顺序改成int b; char a; short c;那大小又会不同b 偏移 0-3a 偏移 4c 偏移 6-7总大小 8。这就引出一个工程结论声明结构体成员时按占用空间从大到小排列能有效减少填充字节浪费。游戏客户端里大量使用结构体做网络协议序列化这个细节直接关系到内存占用和带宽笔试考它不是没道理的。我当时看到这道题第一反应就是想起自己写过的网络消息结构体那次不小心把一个 uint8 放在两个 uint32 中间内存多了一倍。笔试里把它当成算术题来做工作里它就是实打实的性能优化点。2.3 智能指针与手动内存管理ownership 是游戏客户端的心病网易这套卷子对智能指针的考察不是问你 shared_ptr 和 unique_ptr 的区别而是给你一段代码std::shared_ptrMonster m std::make_sharedMonster(); std::weak_ptrMonster w m; m.reset(); if (auto sp w.lock()) { // 这里能不能进 }考的是 weak_ptr.expired 和 lock 的语义以及 shared_ptr 循环引用问题。我当时答题时特别强调了一个场景场景里的 GameObject 和 Component 互相持有 shared_ptr导致节点永远不会释放这就是循环引用。游戏客户端的对象生命周期管理跟普通后台服务不一样。普通服务可以接受对象“晚一点释放”但游戏里战斗单位每秒销毁几百个如果一个怪打完后还赖在内存里帧率就会越来越低。笔试里虽然只考智能指针实际上是在筛你做不做得了资源生命周期管理。我的回答里一般会带一句“shared_ptr 适合跨模块共享适合做 UI 事件回调持有者但场景对象树里更推荐用裸指针 统一容器管理或者使用 handle 下标索引”。这个补充往往能让面试官觉得你有实际项目经验。3. 数据结构和算法不要背题要会推导复杂度3.1 TopK 问题一道题能看出你会不会取舍算法题在这套卷子里占了不少篇幅但网易考得很有游戏特色。比如“海量玩家按战斗力取前 100 名”这种题就比纯粹给一个数组求 TopK 更难一点因为它多了一个隐含条件数据是动态变化的玩家战斗力会实时更新。TopK 的经典解法我记得很清楚线程和分布式思想MapReducestd::priority_queue维护一个大小为 K 的最小堆堆顶是当前第 K 大的元素新元素比堆顶大就替换最后堆里就是 TopK快速选择算法QuickSelect平均时间复杂度 O(n)但工程上要小心最坏情况如果数据量超内存还可以用分桶思路把数据按区间拆分到多个文件分别排序。笔试里你光写“用堆”是不够的要明确说明复杂度建堆 O(K)每个元素和堆顶比较 O(log K)总时间复杂度 O(n log K)。如果数据规模是 1000 万K 100这比全排序 O(n log n) 快得多。这道题背后有个游戏开发场景排行榜服务。玩家战力随时更新客户端要展示全服 Top100。你不能每次都全量排序所以常用做法是“定期重算 增量更新”在客户端本地可能就直接维护一个平衡树或跳表。笔试卷想看到的其实是你有没有这种“数据结构服务于业务约束”的意识。3.2 链表类题目递归和迭代哪个更稳网易对链表挺偏爱可能因为链表节点操作最能考察指针基本功。常见的有反转链表、判断链表有没有环、找两个链表的交点、合并两个有序链表。反转链表递归版本写起来很简洁ListNode* reverseList(ListNode* head) { if (!head || !head-next) return head; ListNode* newHead reverseList(head-next); head-next-next head; head-next nullptr; return newHead; }但我建议你在笔试里写迭代版本因为递归版本虽然短面试官可能会追问“递归栈深度是多少”。如果链表长度是几十万递归直接栈溢出。迭代版本三指针翻转空间复杂度 O(1)这才是工程上可落地的写法。我当时在卷子上还额外写了一句“如果链表节点数不确定优先使用迭代避免调用栈溢出”。这种细节不会扣分反而会让阅卷人觉得你不是只会默写答案。3.3 哈希表的工程变形支持随机返回一个元素网易这套卷子里有一道我印象很深的算法设计题设计一个数据结构支持插入、删除、随机获取一个元素要求三个操作的时间复杂度都是 O(1)。这题的坑在于普通哈希表能做到插入和删除 O(1)但“随机获取一个元素”没法做到因为哈希表没有索引顺序。解法是哈希表 动态数组组合动态数组vectorT存元素保证随机访问 O(1)哈希表unordered_mapT, int存元素到数组下标的映射插入把元素 push 到数组末尾同时记录下标删除把要删的元素和数组末尾元素交换然后 pop_back再更新哈希表里被交换元素的下标随机获取随机生成一个数组下标返回对应元素。这里最容易翻车的是删除操作。如果你直接把元素从数组中间删掉后面的下标全部失效。所以我在卷子上特意用三步写清楚交换、删除、更新映射。这道题考的是组合数据结构的思路也是游戏开发里很常见的一种能力——用一个结构解决不了就组合两个结构。4. 游戏数学与渲染基础从向量点积到坐标空间4.1 点积与叉积不只是公式是游戏逻辑的基石网易这套卷子对游戏数学的考察比我想象中更注重应用。它不会让你背点积公式就完事而是放在一个具体的玩法场景里问你。比如一道题玩家朝正前方发起攻击怪物在某个位置如何判断怪物是否在攻击范围内。这其实是一个扇形判定问题先判断怪物与玩家的距离是否小于攻击半径再用点积判断方向是否在扇形角度内。点积公式是a · b |a||b|cosθ判断方向的思路是取玩家朝向向量forward和玩家指向怪物的向量dir计算forward · dir如果值为正且大于cos(半角)就说明怪物在攻击扇形内。这就是很多近战游戏“背后攻击有加成”的实现基础。叉积的应用也差不多计算一个平面法向量、判断向量的左右关系。比如第三人称相机绕角色旋转时右移和旋转方向的判断就会用到叉积。笔试里你不需要现场推导全部公式但一定要能写出点积、叉积的几何意义并能举一个游戏里用它的例子。4.2 坐标空间变换模型空间到裁剪空间为什么这么多坐标系游戏客户端渲染基础这道题网易很喜欢问坐标空间变换。由模型空间到世界空间再到视图空间最后到裁剪空间每个阶段的矩阵叫什么、为什么需要它这个必须答清楚。我答题时习惯用一句口诀串起来模型空间美术在建模软件里做出来的坐标原点在模型自身世界空间把所有模型摆进同一个游戏世界里每个模型有一个世界矩阵视图空间以相机为原点的空间摄像机朝哪看决定了物体在“观察者眼里”的位置裁剪空间通过透视投影矩阵把视锥体变换成一个标准立方体方便引擎做裁剪和 z 测试屏幕空间最后把裁剪空间里的坐标映射到屏幕上。为什么不能只用一个坐标系统因为不同的计算阶段关心的信息不一样。比如光照计算需要世界空间法线阴影计算经常在视图空间做UI 又要直接操作屏幕坐标。一个游戏客户端开发如果搞不清这些坐标系的切换遇到“模型旋转后位置不对”的问题基本只能靠瞎调数字。4.3 AABB 碰撞检测简单但必须会优化碰撞检测是游戏客户端笔试的经典题AABB轴对齐包围盒是其中最基础的一种。题目一般会让你判断两个 AABB 是否相交。二维 AABB 相交的条件很简单两个矩形在 x 轴上的投影区间和 y 轴上的投影区间都重合。代码可以写成bool Intersect(const AABB a, const AABB b) { return a.minX b.maxX a.maxX b.minX a.minY b.maxY a.maxY b.minY; }但网易一般会加一个小追问如果场景里有 10000 个物体每帧都要做碰撞检测怎么做性能优化。这个会引到广义相和狭义相先做广义相broad phase用空间网格、四叉树、BVH 等快速排除明显不相交的物体再做狭义相narrow phase对少数候选对做精确的 AABB 或三角形级检测。我当时答题时也写了一个小优化技巧把 AABB 判定拆成两个轴分别判断x 轴不相交就直接 return false避免多余的 CPU 运算。这种细节在笔试里很讨巧因为面试官知道你真的用 CPU 性能换过帧率。5. 网络同步与客户端架构简答题里的工程含量5.1 状态同步 vs 帧同步选择题背后是玩法设计网易这套卷子大概率会出网络同步相关的简答题因为这是 MMO 和竞技类游戏绕不开的核心问题。状态同步的经典流量形态是服务器计算完状态后把位置、血量、状态等信息广播给客户端客户端负责展示。帧同步则是服务器只转发操作指令所有客户端在同一帧执行同一套输入跑出相同的结果。这道题的答题框架可以分成四步列出两种同步模式的核心思想对比优缺点给出适用场景结合一款你知道的游戏说明为什么这样选。比如 MOBA 游戏尤其是《王者荣耀》这种早期用帧同步做战斗因为它能把服务器压力降到最低而且 replay 文件很小只需要记录操作。但帧同步有个致命问题不同客户端浮点计算精度不一致会导致状态分叉所以需要确定性浮点库。如果让我站队我会说“没有绝对好坏只有合不合适”。MMO 的开放大世界更适合状态同步因为物体数量多、玩家不在同一场景状态同步便于服务器权威管理而 1v1 或者 5v5 的竞技场玩家少、重操作一致性帧同步更合适。我印象很深的是这道题还有一个隐藏的坑同步频率不是越高越好。把发送频率从 10Hz 提到 20Hz带宽成本会翻倍但玩家体感可能没什么变化。答题时如果能提到“根据玩法和操作灵敏度选择合适的同步频率”这一层会显得你确实做过网络优化。5.2 网络延迟补偿客户端预测与回滚网络同步简答题里另一个高频考点是“玩家操作后感觉有延迟怎么办”。答案是客户端预测、服务器权威、回滚。我按自己的理解总结一下客户端本地先执行输入不等服务器确认立刻更新位置和动画这就是“预测”服务器到达后对比客户端预测的结果和真实结果如果一致就不管不一致就让客户端回滚到服务器位置重新播放后续状态为了让回滚不那么突兀客户端会做插值平滑。笔试答题时不需要把整套代码写出来但要能准确描述这三个步骤并且说明为什么“回滚”不可避免。我记得有一道题专门问“为什么射击游戏里敌人被打中到玩家看到掉血中间会有几百毫秒的时间差”这个就要从网络 RTT 和服务器处理时间角度解释。5.3 客户端架构设计题技能系统或背包系统怎么组织网易笔试的简答题偶尔会出开放设计题比如“设计一个技能系统”。这种题没有标准答案但很能看出一个人的模块划分能力。我的答题思路是定义技能的数据结构包括 ID、名称、伤害、冷却时间、资源消耗定义技能释放流程判断蓝量/冷却 - 播放动作/特效 - 伤害判定 - 结算考虑扩展性用配置文件 行为节点来支持不同技能而不是硬编码在 if else 里考虑网络输入技能释放可能由服务器验证客户端只做表现。如果你有实际项目经验还可以补充提到状态机、事件驱动、组合模式等。我当时就花了不少篇幅写“技能状态机”和“伤害结算时机”因为这是区分纯粹刷题者和真正做过游戏开发的关键。6. 一周冲刺的复习路径与复盘心得6.1 如果时间有限优先级应该怎么排我不是那种建议你从零开始背整本 C Primer 的人。如果你拿到这份卷子时只剩一周我认为复习优先级应该是手写算法代码链表、二叉树、TopK、双指针、动态规划经典题每天至少手写 5 道C 高频概念虚函数、内存对齐、智能指针、const、引用与指针、静态成员、构造函数析构顺序游戏数学向量点积叉积、矩阵变换、坐标空间、四元数的基本概念渲染基础渲染管线、光照模型、AABB 碰撞网络同步状态同步 vs 帧同步、客户端预测、服务器权威。我见过太多人把大量时间花在啃引擎源码上结果笔试第一页的 C 题都做不顺。先把基础和算法搞定再去看引擎卷面分数会好看很多。6.2 我踩过的坑和总结的答题习惯最后分享几个我在笔试和面试里总结出来的习惯未必适合所有人但对网易这套卷子肯定有用第一答题时把思路写清楚不要只丢代码。算法题阅卷的时候不仅看结果对不对还会看你的注释和边界条件处理。我一般会在代码块旁边用中文把复杂度写清楚比如“每次操作 O(1) 摊销”这样阅卷人知道你心里有数。第二不确定的概念不要硬写。网易的卷子喜欢在概念题后面加一个小问你要是前面答错了后面内容也跟着崩。我当年有个朋友把“栈”和“堆”的默认大小答反了后面所有内存分配相关的延伸题基本都废了。第三准备一两个自己真实做过的项目细节。很多简答题的落脚点是“你实际怎么做的”。你不需要拿 3A 大作项目压场一个自己写的 2D 小游戏 demo只要能讲清楚技能系统怎么做的、碰撞检测怎么优化的都比背书有价值。第四心态上把它当一次自我诊断。网易这套笔试卷的整体难度在游戏行业校招里算是比较扎实的如果你做起来觉得有些地方完全没见过不代表你不行只代表你的知识体系里还有几块需要补。我当时做完觉得网络同步那块特别虚后来恶补了两个月面试聊到 MOBA 同步方案时反而成了我的加分项。游戏客户端这条路就是这样笔试不是终点它只是逼你把基础打得更牢的一道关卡。
返回列表