西山居游戏开发岗笔试复盘:从C++基础到图形学与游戏逻辑设计
2026/9/4 15:43:08 网站建设 项目流程

去年秋招,我投了西山居的游戏开发岗,笔试是在一个工作日的晚上进行的。打开在线测评链接的时候,我还以为自己准备得挺充分——LeetCode刷了两百多道,C++八股背了好几轮。结果题目加载出来的那一刻,我盯着屏幕愣了好一会儿:这卷子和普通互联网大厂的笔试题,完全是两个物种。整场笔试两个小时,题型既有选择题,也有代码题,还有让我现场设计游戏逻辑的题。说实话,那两小时比我在球场打一下午全场还累。

这篇文章就是想把这份笔试的完整复盘写出来。适合谁看?如果你准备投游戏公司的技术岗,尤其是西山居这类有自研引擎、做大型MMO的厂商,这份经验能帮你把复习方向从"通用算法题"掰回到"游戏开发场景"上。我会把考察侧重点、典型题型、现场时间分配、以及我踩过的坑,全部拆开来讲。

1. 笔试前必须先摸清的:岗位方向与考察侧重点

很多人拿到笔试邀请就开始刷题,这是最大的误区。游戏开发岗不是一个笼统的岗位,它内部至少可以拆成客户端开发、引擎开发、服务端开发三个方向,每个方向笔试试卷的侧重点差异很大。你投的到底是哪个方向,决定了你的复习重心。

1.1 客户端、引擎、服务端:三套不同的考察思路

西山居目前的业务线里,既有《剑网3》这种基于自研引擎的端游项目,也有《尘白禁区》这类Unity技术路线的产品。客户端开发更多关注玩法系统实现、UI逻辑、战斗表现、场景交互;引擎开发则会更深入地考察渲染管线的理解、资源管理、内存布局、跨平台适配;服务端方向更侧重高并发、数据库、网络同步、分布式状态管理。

笔试虽然是一张卷子,但每个方向的题目占比是有讲究的。我印象比较深的一点是:客户端方向的题目里,场景管理和物体交互的代码题会多一些;引擎向的岗位,图形学和内存相关的内容会明显加重。服务端方向虽然也有C++题目,但多线程、锁、网络协议的比例会更高。

提示:投简历的时候确认一下邮件里标注的技术栈关键词。如果写的是"U3D客户端",那Unity生命周期、组件系统、物理射线检测几乎必考;如果写的是"引擎研发",那图形学基础的分量会重很多。

1.2 从笔试邀请里反推复习优先级

笔试邮件里通常会有几个关键信息:总时长、题型说明、编程语言限制、是否允许本地IDE。这些信息都能帮你规划复习策略。

  • 总时长两小时,题目数量在三十题左右,意味着选择题不能恋战,平均每题只有几十秒。
  • 允许使用的编程语言一般明确写了C++,个别岗位会放宽到C#。如果你只会刷Python的算法题,这时候会很难受——很多游戏开发岗位笔试根本不给Python选项。
  • 题目如果包含"游戏场景/逻辑设计"类的主观题,一般不是考察你背了多少API,而是看你面对一个开放性问题时,能不能把问题拆解成可实现的系统模块。

我当时就是从邮件里看到"线下笔试,仅允许C++,含图形学基础题"这几个字,才意识到复习方向必须从纯刷题转向"游戏开发视角的C++"。如果你现在还没收到笔试邀请,建议先按这个思路去准备,比临时抱佛脚强得多。

1.3 游戏公司笔试到底在筛什么样的人

这是很多人没想明白的问题。游戏开发岗笔试和互联网通用后端笔试的核心区别,在于前者更看重空间想象能力场景抽象能力对实时系统的敏感性。同样是考算法,游戏公司会把它包装成"场景里有N个敌人,怎么快速找到玩家周围的敌人";同样是考C++,它会问你"一个游戏对象池怎么实现,才能避免频繁new和delete导致的性能抖动"。

换句话说,游戏公司笔试筛选的不是"刷题机器",而是"具备游戏开发思维的人"。这种思维包括:理解CPU与内存对实时渲染帧率的影响,理解Update和FixedUpdate的差异对物理模拟的意义,理解为什么游戏服务器需要做状态同步而Web后端大多不需要。这些东西不是靠最后一晚突击能补上的,需要提前一两个月开始用"游戏开发的眼睛"去审视你学过的每一个知识点。

2. 题型地图:三块内容各自在考你的什么能力

把笔试当成一个完整的关卡来解,首先要看它的关卡组成。西山居游戏开发岗的笔试题型,我把它归纳成三大块:选择题、编程题、游戏逻辑/设计题。每一块的考察目标完全不同,答题策略也完全不同。

2.1 选择题:基础不牢的照妖镜

选择题的覆盖面非常广,但难度通常不会超过"概念理解"层面。常见的出题点包括:

  • C++语言基础:虚函数机制、智能指针用法、const修饰符、左值右值与移动语义、模板特化。
  • 数据结构与算法概念:排序算法的时间复杂度、哈希表冲突处理、红黑树和AVL树的区别、堆与栈的区别。
  • 操作系统基础:进程与线程的区别、死锁的四个必要条件、虚拟内存、页表。
  • 计算机图形学基础:渲染管线的几个阶段、透视投影与正交投影的区别、颜色空间、纹理采样与过滤方式。
  • 游戏引擎常识:Unity的脚本生命周期、预制体的概念、场景管理方式、资源加载与卸载。

这里有个很重要的判断:选择题考的不是你背得熟不熟,而是你在考试压力下能不能快速回忆起概念并做出判断。游戏开发是实时交互系统,代码出Bug不会等你慢慢想,所以笔试中设置大量选择题,模拟的就是那种需要在短时间内做出准确判断的工作状态。

2.2 编程题:从暴力到优化的完整踩分逻辑

编程题一般是两到三道,难度从"经典算法题"到"游戏场景包装题"不等。很多人一看到"游戏场景包装"就直接发懵,其实剥开包装之后,核心算法往往是你见过的经典模型。

举个例子,题目可能会这样出:你设计一个塔防游戏,地图上有若干个防御塔,每个塔有固定的攻击范围,现在给你一批怪物的坐标,要求快速判断每个怪物会被哪些塔攻击。剥开这层皮,本质就是一个空间范围查询问题,可以用网格分桶、四叉树或空间哈希来解决。如果你只学了裸的算法,没有做过"把算法嵌入到游戏逻辑"的训练,这种包装题很容易把你带偏。

编程题的判分方式也值得注意。在线笔试的判题系统一般不会因为你的代码编译不过就直接给零分,很多时候是按测试用例通过比例给分的。这意味着:即使你只能写一个暴力解法,也要把它写出来提交,至少能拿到一部分分数。

2.3 游戏逻辑/设计题:笔试真正的分水岭

这一块是我认为最能拉开差距的部分。它通常不是让你写完整代码,而是给你一个开放式的游戏设计问题,要你用文字或伪代码描述你的实现思路。

我考完复盘,这类题的核心套路可以归纳成三步:拆解需求、确定技术选型、描述容错方案

拆解需求就是要把题目描述中的高维目标拆成可执行的功能模块。确定技术选型就是针对每个模块选择合适的工具或实现路径。描述容错方案是很多应届生最容易忽略的——比如"如果一个玩家掉线了,下次重连回来,他的状态应该怎么恢复"。游戏是和"人"打交道的系统,异常处理比功能实现更能体现工程能力。

3. C++与数据结构的常考考点:附一道典型题的完整复盘

既然游戏开发岗笔试普遍限制C++,那C++语言本身的功底就是第一道关。这一节我结合西山居笔试的考察风格,挑几个核心考点展开讲,并拆解一道非常典型的编程题解题过程。

3.1 C++基础为什么反复被问:虚函数、智能指针、内存管理

游戏客户端对性能极度敏感,C++的每一个语言特性都直接和内存布局、CPU缓存、虚函数调用的开销挂钩。

  • 虚函数与多态:场景中可能有几十种敌人类型,都用基类指针管理。虚函数表怎么布局?虚函数调用的性能开销在哪里?为什么有些游戏引擎会尽量减少虚函数调用次数?
  • 智能指针与内存管理:Unity有自动GC,但C++引擎中内存管理完全交给开发者和智能指针。shared_ptr的循环引用问题怎么解决?weak_ptr在场景对象的观察者模式中有什么作用?游戏场景中一个敌人被销毁了,但它还被技能系统引用着,这个悬垂引用怎么避免?
  • 移动语义与右值引用:渲染线程每帧会创建大量的临时变量,如果在传递过程中触发深拷贝,性能会急剧下降。移动语义做字符串、容器的零拷贝传递,是面试官非常喜欢的考点。

这些知识点不能只背概念,一定要能做"为什么游戏开发需要它"的关联。西山居这种做大型MMO的公司,渲染线程、逻辑线程、网络线程并发工作,内存管理和对象生命周期几乎刻在每一行代码里,所以笔试反复考这些,完全在情理之中。

3.2 一道图的遍历题:从读题到最优解的思考链路

笔试编程题中给我印象最深的,是一道被包装成游戏场景的图论题。题目大意是:一张游戏地图有许多据点,据点之间有道路连接,每条道路的通行时间不同。玩家要从起点据点出发到达终点据点,但部分道路因为怪物封锁暂时不能走。求玩家到达终点的最短时间。

剥开包装,这是一道标准的单源最短路径问题。但在考场环境下,完整的思考链路应该是这样:

第一步,先判断数据规模和起终点数量。如果只有几百个据点,Floyd-Warshall也能过,但更好的是Dijkstra。如果起终点只有一对,直接Dijkstra;如果有多组查询,可以考虑预处理或者Floyd。

第二步,考虑封锁道路。封锁道路在图论中就是边的失效。这是最容易踩坑的地方——如果你在Dijkstra过程中动态调整边权,堆中存储的旧距离可能失效,需要专门的失效处理。更稳的方法是:在初始化时就把不可用的边排除掉,不加入邻接表。如果封锁是动态的,可以把不可通行的时间区间记录在边上,做"带时间窗的最短路径",但这通常不是笔试难度。

第三步,选择数据结构。用C++的话,优先队列加邻接表是标准做法。堆中存pair(当前距离,节点编号),每次取出当前最小距离的点,松弛它的邻接边。注意pair在C++中比较时是先比较first再比较second,所以用距离做first即可。

我给出的解法核心如下:

typedef pair<int, int> pii; // (distance, node) vector<int> dijkstra(int n, int start, const vector<vector<pii>>& graph) { vector<int> dist(n, INT_MAX); dist[start] = 0; priority_queue<pii, vector<pii>, greater<pii>> pq; pq.push({0, start}); while (!pq.empty()) { auto [d, u] = pq.top(); pq.pop(); if (d > dist[u]) continue; // 堆中的旧数据,跳过 for (auto& [v, w] : graph[u]) { if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; pq.push({dist[v], v}); } } } return dist; }

这段代码里有两个细节值得留意:一是if (d > dist[u]) continue;这一行,这是Dijkstra用优先队列时的经典剪枝,不写的话虽然结果可能不错,但堆中会积压大量过期节点,浪费时间和内存;二是把边信息直接存成pair<int, int>,在笔试环境下简单清晰,比自定义struct更方便。

提示:笔试时如果时间紧张,优先保证用Dijkstra能过所有测试用例,而不是去优化SPFA或者A*。A*虽然更快,但需要设计启发函数,在考场上风险更高,除非你训练过很多次,否则别轻易用。

3.3 刷题优先级:把力扣题单和游戏开发对齐

如果准备时间有限,我不建议你按照力扣热题100的顺序从头刷到尾。你可以按下面这个优先级来安排:

优先级考点力扣高频题类型为什么游戏开发岗重视
最短路径Dijkstra、Floyd寻路、地图、AOI场景的基础
并查集判定连通块数量技能范围、区域划分、服务器分线
二叉堆TopK、优先队列战斗中的目标选取、AI决策排序
动态规划背包、状态压缩背包系统、养成系统数值计算
二分查找答案二分、分数规划伤害计算、匹配系统、雷达范围
哈希表/字典空间换时间道具索引、NPC索引、对象池查找
字符串算法KMP、Trie文本配置表、技能名匹配、聊天系统

这不是说冷门算法不重要,而是从投入产出比来看,上述这些高频考点覆盖了游戏开发中最常见的场景。如果你刷了两百道题还没碰到过并查集和Dijkstra,那效率确实有点低,建议有针对性地补一补这两个专题。

4. 图形学与引擎基础:背了就能拿分的常青考点

图形学基础是游戏开发岗笔试区别于普通软件岗笔试的标志性内容。这部分其实不需要你真正用OpenGL写过一个渲染器,只要把核心概念和它们在真实渲染管线中的位置搞清楚,就能拿到大部分分数。

4.1 数学与渲染基础:坐标系、矩阵、渲染管线

图形学的核心是数学。笔试最常考的数学概念包括:

  • 齐次坐标:为什么三维空间中的点要用四维坐标表示?因为平移变换不能用3x3矩阵表示,加一个维度后,平移、旋转、缩放可以统一用矩阵乘法实现。
  • MVP矩阵:顶点从模型局部坐标(Model)经过模型矩阵变换到世界坐标(World),再经过视图矩阵变换到观察坐标(View),最后经过投影矩阵变换到裁剪坐标(Clip)。你只需要记住每个矩阵的职责,考试就够用了。
  • 渲染管线阶段:从顶点着色器到光栅化,再到片元着色器,然后经过深度测试、模板测试、混合,最后输出到帧缓冲。笔试中常问"一个三角形从CPU到屏幕经历了哪些阶段",这就是标准答案。

这些概念听起来抽象,但你只要玩游戏就能找到对照:你操控的角色站在场景中,它的位置是局部坐标;当它走过一座桥,位置在世界坐标中变化;你屏幕上的画面是摄像机观察的结果,这是观察坐标;最终显示在2D屏幕上的像素,是经过了投影变换和光栅化的结果。

4.2 引擎生命周期与场景管理:Unity和Unreal的底层逻辑

引擎相关的选择题非常直白,基本就是在考生命周期和对象管理。

Unity的脚本生命周期几乎是必背内容:Awake在对象创建时被调用,OnEnable在对象启用时调用,Start在第一次Update之前调用,FixedUpdate以固定时间步长调用,适合物理计算,Update每帧调用,适合逻辑更新,LateUpdate在Update之后调用,适合相机跟随。笔试会故意在FixedUpdateUpdate之间做文章,比如"刚体运动相关逻辑应该放在哪个函数里",答案就是FixedUpdate,因为物理引擎的步长是固定的,放在Update里会导致物理模拟不稳定。

对象池也是游戏开发笔试的老熟客。为什么需要对象池?因为频繁InstantiateDestroy会产生内存碎片,也会触发GC,导致渲染卡顿。对象池的思想是:预先创建一批对象,使用的时候从池中取出,用完归还,而不是销毁。把这个思想写成C++代码,核心就是内存块的复用。

4.3 把知识点串起来:答题怎么说才显得你是真懂

别背结论,你要把结论背后的因果链讲出来。比如选择题问你"场景中大量对象连续创建和销毁会导致什么问题",你不仅要知道答案是"内存碎片和GC压力",还要理解"连续创建和销毁会产生大小不一的空闲内存块,新对象因为找不到连续内存块而触发更大规模的内存整理,这个整理过程会打断渲染线程"。能把因果链讲清楚,哪怕题目换个说法,你也能答对。

笔试中遇到这类问题,我的表达策略是:先说结论,再补一句机制,最后提一个工程上的应对。比如:"对象池可以解决这个问题,Unity中可以用Stack存储闲置对象,配合预制体和协程做预热加载。"

5. 笔试现场的答题策略与时间管理

知识储备是底线,但能不能在有限时间内把储备兑现成分数,靠的是现场策略。这一节分享我经过验证的做卷顺序和时间分配方法。

5.1 做卷顺序:先扫选择题,再啃编程题,最后留时间给设计题

我的建议是:先花15到20分钟把选择题快速过一遍,然后立刻做编程题,最后留出大块时间处理游戏逻辑设计题

为什么是这个顺序?选择题考察的是知识覆盖面,每一题都是独立的,不依赖其他题目的进展。快速扫一遍,会做的直接选,不会的标记一下先跳过,这样能保证你在状态最好的时候拿到基础分。编程题需要读题、分析、编码,是全场最耗时的环节,必须在时间充裕时做,避免快到交卷了才草草写两行。游戏逻辑设计题虽然分高,但它不需要精确的代码细节,主要靠思路,即使最后剩下二十分钟,也能写出有价值的文字说明。

5.2 编程题的提交策略:先暴力,再优化

在线笔试的判题系统是按测试用例给分的。这意味着你完全可以用"逐步踩分"的思路来答题:

  1. 先写一个暴力解法,确保能过最小规模的数据。
  2. 用示例测试用例自测一下,确认输入输出格式完全正确。
  3. 再在暴力解法的基础上,逐步加入优化:比如把线性查找改成哈希表、把递归改成迭代、把两层循环降到一层。
  4. 如果优化的版本没写完,至少还有一个正确但慢的版本兜底。

这种方法最重要的价值是心理层面的。很多同学拿到一道题,哪怕暴力解一眼就能想出来,也非要直接想最优解,结果悬在上面,连暴力分都没拿到。游戏开发是一个需要不断迭代的项目流程,先跑起来再优化,这个思维在笔试中也完全适用。

5.3 遇到不会的题:写思路、写伪代码,别留白

编程题经常会遇到卡壳的情况。我的经验是:不要干坐着。哪怕写不出完整代码,也可以在答题框中写出你的解题思路、时间复杂度分析、甚至伪代码。部分在线笔试系统允许这类文本答案被人工判卷看到,就算系统不认,答题框里有文字,也代表你具备分析问题的能力。

注意:如果笔试环境是白板式网页编辑器,没有本地编译调试功能,千万不要依赖编译器来检查语法。平时练习时就养成"一次性写出接近正确的代码"的习惯,注意括号匹配、分号结尾、头文件包含这些细节,考场上能省出大量宝贵时间。

6. 复盘:我踩过的坑和你可能也会踩的坑

这一节算是付费内容了。笔试结束后我认真复盘了几个典型的翻车点,把它们写在这里,希望你在考场别踩同样的坑。

6.1 在线笔试环境不熟悉,白白浪费窗口期

我第一次做在线笔试时,系统用的是网页编辑器,编译和运行在云端,输入测试用例还要手动粘贴到特定输入框里。我用本地IDE习惯了,结果切到网页编辑器后,连头文件都忘了怎么包含。最尴尬的是,我写了十分钟的代码,才发现自己一直没点"保存"按钮。

建议:笔试前一定要去牛客网、力扣等平台做几次在线模拟笔试,把输入输出格式、代码提交方式、自测用例的操作手感提前练熟。至少要知道"如果代码编译失败,能不能看到编译错误信息""能不能在运行窗口输入多行测试数据"。

6.2 太依赖IDE调试,切到白板环境慌了

我的另一个坏习惯是平时写代码特别依赖IDE的断点调试和变量监视。笔试环境没有这些工具,我写Dijkstra那道题时,本地跑了三遍才通过测试用例,但网页编辑器里我只能靠printf打日志来调试,效率低了很多。

现在回想,平时写代码应该刻意练习"一次性写对"的能力:先在纸上或注释里把逻辑框架写清楚,再落代码。这样依赖IDE调试的习惯,会在真正的面试手撕代码环节吃大亏。

6.3 复习投入错位:选择题花太多时间,编程题练得不够

我前期把大量时间花在背八股文上,C++的内存模型、虚函数表、进程通信背得滚瓜烂熟。但笔试真正拉开差距的,其实是那两道编程题。尤其是第二道带时间窗的图状包装题,我花了将近四十分钟,最终还没有把边界情况处理完,导致部分测试用例没通过。

如果重来一次,我会把复习时间分为60%刷题、30%巩固基础、10%看游戏开发相关的技术分析文章。基础知识点像是内功心法,但笔试分数大头仍然是看得见的代码。这个比例更实用。

6.4 游戏逻辑设计题的答题深度不够

答题时我把游戏逻辑设计题当作普通主观题来答,写了几句"判断距离,若小于攻击范围则受到伤害"这种泛泛的话。考完复盘发现,面试官想看到的不是一句话结论,而是完整的模块拆解:敌人在服务端管理还是客户端管理?攻击范围的检测是每帧遍历还是空间分区?多个敌人同时攻击同一个玩家时,伤害结算顺序怎么定?这些工程细节,才是拉开分数的地方。

所以遇到这类题,我建议你按这个框架来组织答案:需求拆分、数据结构选型、主循环整合、异常处理、可能的性能优化点。别怕写得多,关键信息一步到位,反而能体现出你的工程视野。

最后再分享一个细节:我那场笔试特别考了一个"玩家中途掉线重连后如何恢复战斗状态"的小问。这个问题很多人答的是"存个档"。但如果理解MMO架构,就会想到服务端权威架构下,战斗状态本来就由服务端维护,重连只需要在客户端重建表现层状态、重新同步位置和血量即可。这种认知差异,可能就决定了你最终能不能拿到面试邀请。希望你在准备笔试的过程中,不只是刷题,也多想想一个系统在真实游戏项目中是怎么被运转起来的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询