又到了一年校招季,后台经常有学弟学妹来问游戏公司笔试怎么准备,尤其是吉比特这种产品和研发都比较有特色的厂商。2018届校招技术类笔试B卷,我当时认认真真做过一轮复盘,后来也帮几个朋友拆过这套卷子的出题思路。今天干脆把这份复盘整理出来,给准备技术岗校招的同学做一个方向性参考,尤其是想投客户端、服务端、引擎这类研发岗的,可以先对照着查漏补缺。
先说明一点,各年的题目和题型分布官方不会公开完整真题,我这里梳理的是基于笔试常见考察方向、当年考生反馈以及游戏行业技术笔试通用逻辑的复盘。重点不在于背题,而是帮你搞清楚这类笔试到底在筛选什么人、每一类题背后的考察意图是什么,然后照着这个方向去准备,效率会高很多。
1. 先说说这张卷子的整体印象:B卷到底在考什么
1.1 游戏公司笔试和其他互联网公司笔试有什么不一样
很多人第一次做游戏公司的笔试,会下意识拿互联网大厂的标准来衡量,其实两者差异不小。大厂笔试更多是通用素质筛选,数据结构和算法题占比极高,甚至有的就是纯算法题海。游戏公司不一样,尤其是吉比特这类有自研产品线的公司,笔试明显能看出一种倾向:他们不是在找“竞赛型选手”,而是在找“能进游戏研发流水线干活的人”。
这话怎么理解?我拆几个点。
第一,算法题要考,但往往不会考到特别偏门的程度。像红黑树手写、后缀自动机这种在部分大厂笔试里可能出现的题,在游戏公司笔试中很少见。更多是链表、二叉树、动态规划、图的最短路径这类“生产环境真正会用”的算法。因为游戏研发里大量场景,比如角色寻路、NPC行为树、战斗伤害计算、背包排序,用到的基本都是这些基础算法,他们需要的是你能把这些基础东西在工程里写对、写稳,而不是炫技。
第二,C++相关的基础问题出现频率非常高。吉比特在早期端游时代积累了比较深的C++技术栈,客户端、服务端核心逻辑大多跑在C++上,所以笔试里对C++的考察不是简单的“你学过吗”,而是“你敢不敢在生产环境里用”。虚函数、内存管理、智能指针、STL容器底层原理,这些几乎是必考方向。
第三,会有少量游戏研发特有的场景题,这是和非游戏公司最大的区别。比如帧同步和状态同步的对比、资源加载策略、热更新方案、渲染优化手段、AOI算法这类。这些东西在通用互联网公司的笔试题里几乎不会出现,但对游戏公司来说却是基本功。
B卷当时通常用在研发类技术岗位上,和A卷相比,整体更偏向工程实现和综合理解,纯背诵型题目相对少一些,很多选择题表面上是考概念,实际上一错就错在“理解没到位”。
1.2 从卷面结构反推考察逻辑
综合来看,吉比特这类游戏公司技术笔试的卷面结构大致可以分为四块:选择题、简答/填空题、编程题、综合设计题(不一定每场都有,但出现概率不低)。
- 选择题:覆盖C++语言、数据结构、操作系统、网络、数据库,考点广但深度适中。这部分既要拼基础扎实程度,也拼审题仔细程度,因为游戏公司很喜欢在选项里埋“小陷阱”。
- 简答/填空题:概念解释类为主,比如“简述TCP三次握手的过程”“什么是虚拟内存”“谈一谈你对智能指针的理解”。乍一看是送分题,实际上考察的是能不能用严谨、有条理的语言把概念讲清楚。
- 编程题:一般是2到3道,难度呈阶梯分布。第一道往往是链表、字符串、简单动态规划,后一道可能会结合具体场景,比如寻路或者任务调度。
- 综合设计题:不是每张卷子都有,一旦出现往往是区分度最高的题。比如“如果让你设计一个MMORPG的背包系统,你会怎么设计数据结构”“描述一下你理解中的帧同步”,这类题目没有标准答案,但极能看出一个学生的工程思维边界。
我当时做完复盘之后有一个明显的感觉:这套卷子的目的不是把你考倒,而是把你的知识底子翻出来。会就是会,不会一猜就能露出马脚。所以与其去押题,不如老老实实把每个方向的核心知识夯实。
2. 考点深度拆解:那些容易踩坑的题目方向
2.1 数据结构与算法:核心中的核心
这一块没什么好回避的,就是笔试的重头戏。但游戏公司怎么考算法,和想象中不太一样。我的总结是:广度要求不低,深度要求适中,但工程化要求很高。
所谓广度,是指链表、栈、队列、二叉树、堆、图、哈希表、字符串匹配这些都要覆盖到。常见题型有:
- 单链表反转、判断链表是否有环、找环入口;
- 二叉树前中后序遍历、层序遍历、二叉树深度、最近公共祖先;
- 快排、归并排序、堆排序的手写和复杂度分析;
- BFS/DFS、Dijkstra、A*寻路的基本思想和适用场景;
- 01背包、最长公共子序列、编辑距离等经典动态规划问题。
所谓深度适中,是说很少要求你在考场上推导一个没见过的复杂状态转移方程,更多是考察经典模型你有没有掌握。所以复习的时候重点不是刷多少道偏题怪题,而是把每个经典模型吃透,能快速识别题目属于哪一类,然后套用对应的解法框架。
工程化要求则体现在边界条件上。比如链表反转,很多人能写出主逻辑,但传入空链表或者只有一个节点的时候,返回值对不对?循环终止条件会不会导致死循环?这些细节在本地编译时不一定暴露,但在笔试的在线判题系统里一跑就露馅。
另外一个容易被忽略的点是:游戏公司特别喜欢考“寻路”相关的题目。这不奇怪,游戏里角色从A点走到B点,核心就是寻路算法。准备笔试的时候,至少要把BFS和A的核心思想写熟。A不要求能默写出完美代码,但至少要能说清楚启发式函数h(n)的作用、open list和closed list分别是什么、为什么A*在网格地图中比Dijkstra更高效。
2.2 C++与编程语言基础:游戏研发的硬门槛
如果说算法题决定你能不能过笔试,那C++基础题就决定你过笔试的同时会不会给面试官留下好印象。游戏公司对C++的考察深入程度,通常会超出很多学生的预期。
常见的考察方向包括:
- 指针和引用的区别、const的各种用法、指针数组和数组指针的区别;
- 内存布局:栈区、堆区、全局区、代码段,以及new/delete和malloc/free的本质区别;
- 虚函数机制:虚函数表怎么工作、为什么析构函数建议声明为虚函数、纯虚函数和抽象类的关系;
- 智能指针:shared_ptr、unique_ptr、weak_ptr的底层实现原理,循环引用问题怎么解决;
- STL容器:vector底层是动态数组、list是双向链表、map通常基于红黑树、unordered_map是哈希表,各自的插入/查找/删除时间复杂度和适用场景。
我见过太多人在“vector扩容机制”这道题上翻车。很多人知道vector会翻倍扩容,但具体怎么搬移、什么时候触发、迭代器为什么会失效,说得一知半解。实际上,vector在容量不足时会申请一块更大的内存,然后把旧元素拷贝或移动到新内存中,最后释放旧内存,这个过程如果元素是自定义类型,还可能涉及移动构造和拷贝构造的选择。笔试不一定考到这么深,但复习的时候如果能理解到这个层面,选择题基本不会错。
还有一个容易被忽略但游戏公司很爱考的点:内存管理。因为游戏是长期运行的程序,内存泄漏是绝对不能容忍的问题。笔试题里经常会给出一个小段代码,让你分析是否存在内存泄漏,或者让你说说排查内存泄漏的思路。这时候如果你能提到RAII、智能指针、Valgrind、AddressSanitizer等工具和手段,会明显加分。
2.3 操作系统、计算机网络与数据库:基础中的硬通货
这一块表面上看是纯计算机基础知识,但游戏公司考的时候会不自觉地带入游戏场景,需要留意。
操作系统方面,进程和线程的区别是必考,但游戏公司更关注“多线程在游戏研发中的应用”。比如游戏主循环通常是单线程,但资源加载、网络IO、音频解码等任务会放到工作线程中,这时候就涉及线程安全、锁竞争、原子操作。所以复习信号量、互斥锁、条件变量的时候,可以想一想这些机制在游戏加载场景中怎么用,理解会更深入。
死锁也是高频考点。经典的四个必要条件是互斥、持有并等待、不可剥夺、循环等待。笔试中可能会给一个多线程加锁的代码片段,让你判断是否可能死锁。这种题不能只看代码表面,要想清楚两个线程获取锁的顺序是否可能交错。
内存管理里,虚拟内存、页表、缺页中断这些概念要能说出来。游戏引擎的资源管理经常映射到内存映射文件,所以理解虚拟内存机制对后续做引擎开发也有帮助。
计算机网络方面,TCP三次握手和四次挥手是送分题,但别只背状态迁移图,要想清楚为什么需要第三次握手、TIME_WAIT状态出现在哪一端、为什么需要等待2MSL。UDP和TCP的对比也是老生常谈,但游戏公司更喜欢结合帧同步和状态同步来考。比如帧同步为什么用UDP更多、状态同步为什么TCP更常见,这需要你既懂网络协议,又懂游戏同步机制。
HTTP可能考一些常见状态码的含义、GET和POST的区别、HTTP和HTTPS的差异。数据库方面,索引的底层结构(B+树)、聚簇索引和非聚簇索引的区别、事务的ACID特性、SQL语句的编写和优化,都是经典考点。游戏公司对数据库的要求没有电商、金融那么深,但服务端岗位至少要会用SQL做增删改查和简单联表查询。
2.4 游戏研发特色内容:这块最见真章
这是游戏公司笔试里最有辨识度的部分。很多人前面答得不错,一到场景设计题就卡壳,原因不是不懂技术,而是缺乏游戏研发的上下文。
一类常见问题是同步机制。帧同步和状态同步,几乎是游戏服务端和客户端岗位必考的概念。简单的理解是,状态同步是客户端把操作发给服务器,服务器计算后把最终状态广播给所有客户端;帧同步则是服务器只转发操作指令,每个客户端各自执行相同的逻辑,保证输入一致的情况下所有客户端状态一致。笔试可能会问“两种方案的优缺点”“什么类型游戏适合哪种方案”。这个问题需要结合游戏类型来分析,MOBA、格斗游戏、RTS对同步精度要求高,帧同步有优势;MMORPG对实时性要求没那么极致,状态同步更好维护。
另一类常见问题是性能优化。比如渲染优化,可能会问“Draw Call是什么”“如何减少Draw Call”“静态合批和动态合批的区别”,这需要你对渲染管线有一定了解。物理方面,可能会问“碰撞检测有哪些常用算法”“AABB和OBB的区别”。逻辑层优化,可能会问“对象池是什么,为什么游戏里要大量使用对象池”。这些问题只要平时有接触Unity或Unreal,多少都能说上几句,但要说得好,需要真正理解背后的性能动机。
还有一类是架构设计题。比如“设计一个技能系统”或者“设计一个背包系统”,这种题考察的是抽象能力和数据建模能力。以背包系统为例,至少要考虑:物品的数据结构(ID、类型、堆叠数量、品质)、物品格子的组织方式(数组还是哈希表)、背包排序的实现、物品的增删改查操作如何和UI解耦。如果能进一步提到用配置表驱动物品属性、用事件系统通知UI刷新,那就超出大多数应届生的水准了。
3. 实操复盘:做题顺序与答题策略
3.1 选择题与简答题的答题节奏
笔试时间是有限的,哪怕是基础很好的同学,不控制节奏也很容易翻车。我的建议是做选择题时遇到犹豫不决的,先标记出来,不要花超过一分钟硬磕。因为选择题有时候就是考察某个细节记忆,一时想不起来,放在后面可能通过其他题目给到提示。
吉比特这类游戏公司笔试题里,选择题的选项设计有一点和普通互联网公司不同:他们很喜欢在题目里埋“题干陷阱”。比如“以下哪种说法不正确”“以下哪种做法不能提升性能”,这类否定式问法一定要圈出来,我见过不少人在“不正确”这种字眼上栽跟头。
简答题的作答要注意结构。不要只写结论不给理由,比如“TCP三次握手过程”,如果只写“客户端发SYN,服务器回SYN+ACK,客户端发ACK”,那只能算合格,但不够出彩。更好的做法是把这个过程展开:为什么需要第三次握手(防止已失效的连接请求突然传到服务器引起错误)、如果第三次握手丢了会怎样、SYN Flood攻击是怎么利用这个机制的。能把概念讲到这个深度,才说明你是真懂而不是背过。
选择题和简答题的时间分配,建议控制在总时长的40%到50%之间。因为编程题往往需要留出大块时间,尤其是debug过程很耗时。
3.2 编程题的规范作答与方法论
编程题是笔试中拉开差距的关键。我的做题经验是,不管题目难不难,先做三件事:读题、确认边界、设计用例。
读题不能只看一遍,至少要确认三件事:输入是什么类型(整数、字符串、数组)、输出要求是什么格式(打印结果、返回特定值)、有没有特殊限制(时间复杂度要求、空间复杂度要求)。很多在线判题系统对输入输出格式要求很严格,输出多了一个空格或者少了一个换行都可能被判错。
边界条件是最容易扣分的地方。比如求数组最大值,空数组怎么处理?链表反转,只有两个节点怎么处理?二叉树遍历,根节点为空怎么处理?这些东西在本地IDE里跑的时候不会暴露,因为你是自己构造的用例,但判题系统会用各种边界用例去验证。
我拿一道经典题目举例:反转链表。很多人的第一版代码长这样:
ListNode* reverseList(ListNode* head) { ListNode* prev = nullptr; ListNode* cur = head; while (cur != nullptr) { ListNode* next = cur->next; cur->next = prev; prev = cur; cur = next; } return prev; }这段逻辑基本是正确的,但如果再加一个条件“每K个节点一组反转,不足K个保持原样”,复杂度就上来了。这种变体题很常见,不只是靠背代码能解决的,需要有扎实的链表操作能力。
写代码的时候,建议先写注释把思路理清楚,再动手实现。不用写太多注释,但关键步骤要标注,这样即使中间写错了,至少阅卷人能看到你的思路。变量命名规范也重要,不要用a、b、c这种无意义命名,用prev、cur、next,一目了然。
复杂度分析要随手写出来。每道编程题做完后,在代码注释里标明时间和空间复杂度,一方面给自己检查是否满足题目要求,另一方面也能体现工程素养。
4. 常见失误与排查技巧实录
4.1 时间分配失误是最常见的问题
我复盘过身边不少同学的笔试经历,发现最典型的失误不是不会做,而是时间分配崩了。有一种情况是选择题上磨太久,一道题犹豫来犹豫去,最后编程题只剩二十分钟,写了个半成品上去。另一种情况是编程题第一道就卡住,死活调不出来,搞得后面简单题也没时间做。
针对这个问题,我给一个比较实用的策略:试卷拿到手,先花三分钟扫一遍全部题目,大致判断每道题的难度和熟悉度。优先做自己最熟悉、确定性能拿分的题,然后做中等难度的题,最后再啃硬骨头。考试不是做研究,追求的是总分最大化,不是每道题都完美。
编程题如果卡住超过十五分钟,建议先跳过。有时候后面题目的语境会给你启发,而且大脑在后台运行的时候,前面卡住的题反而可能在最后灵光一现。
4.2 环境与输入输出问题
在线笔试和本地IDE有很大不同。本地IDE你随便调试,在线判题系统只关心你的代码能不能在限定时间内存下通过所有测试用例。
常见的输入输出坑有几个:第一,读入多个测试用例时,要注意循环条件的写法,有的题目是多组输入直到EOF,有的是一行一个用例;第二,输出格式要和题目要求完全一致,不要自己加提示文字;第三,C++里getline和cin混用要注意缓冲区残留的回车符,这会导致读入错位。
一个实用技巧是,在写读入逻辑前,先手动构造一个最小的输入样例,在纸上推一遍程序会怎么读,再动手写代码。尤其是字符串处理题,比如反转单词、按逗号分隔等,边界情况非常多。
4.3 知识盲区的临场应对
笔试碰到完全没思路的题,不要直接放弃交白卷。选择题可以排除明显错误项提升正确率;简答题哪怕不会,也要把自己能想到的关键词和相关概念写上去,比如题目问“解释一下渲染管线”,你至少可以把顶点变换、光栅化、片元着色这几个词写出来,多少能拿一点分。
如果是编程题遇到完全没想法的,可以退而求其次,写一个暴力解法。有的判题系统会分批给分,暴力解能通过一部分小规模测试用例,也能拿到部分分数。比交白卷强太多。
4.4 笔试前一周的复习策略
最后一周不建议再去啃新知识,重点应该是巩固已经复习过的东西。我的经验是列出四个方向:算法高频题型、C++核心机制、操作系统网络数据库核心概念、游戏开发基础概念。每个方向用思维导图快速过一遍关键词,看到关键词能讲出完整内容,就算过关。
错题本这个习惯很推荐。不只是记录错题,还要记录“为什么错”。是知识点没掌握,还是审题不仔细,还是时间不够?每次笔试后复盘一次,下次就能针对性调整。
5. 笔试之外:从笔试到面试的衔接准备
5.1 笔试后的复盘比分数更重要
笔试结束不等于这件事就完了。我在每次笔试结束后,都会尽快回忆并记录题目,尤其是自己不确定的题和完全不会的题。因为笔试题目很多时候会出现在面试的问题池里,面试官可能会围绕笔试中的薄弱点展开追问。
比如笔试里有一个简答题是“shared_ptr循环引用怎么解决”,你当时答得一般,那面试前一定要把weak_ptr的原理彻底弄懂。因为面试官看到你的笔试记录,大概率会顺着这个方向问得更深。
5.2 项目经历怎么和笔试内容呼应
很多同学的简历上写了游戏相关项目,但笔试和面试的时候却讲不出来和项目对应的技术点。这就很可惜,因为游戏公司面试官问项目,本质上就是想看你的项目里面有没有用到他们考的那些知识点。
比如你做过一个Unity小游戏,那至少可以说清楚:场景里物体的碰撞检测用的什么方案、资源加载怎么管理、UI刷新怎么做、存档是怎么序列化到本地的。每一个点都可以和笔试中的某类考点对应上。如果你能在笔试后的面试环节把这些工程细节讲出来,会立刻拉开和其他候选人的差距。
5.3 了解公司产品与业务方向
吉比特有过《问道》这样长线运营的端游产品,也布局过多条产品线,不同产品的技术侧重点可能不一样。笔试前如果有余力,可以看看目标公司旗下产品的技术特点。
比如端游和手游的客户端技术在同步、内存管理、渲染优化上侧重点就不同,答题时如果能在场景设计题里结合具体产品类型展开,会显得你对公司业务有真实了解。这一点在场景设计题里特别加分。
我在实际准备过程中还有一个心得:不要只盯着官方招聘公告里的岗位要求看,多去翻翻技术社区里做游戏研发的人分享的日常,能帮你建立起对“游戏开发到底在做什么”的真实认知。笔试题目是死的,但对行业的理解是活的,后者往往才是你从大量竞争者中跳出来的关键。