FunPlus 游戏客户端一面:输入适配、Lua 元表与 LRU 场景
- 轮次
- 一面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 自我介绍。
- 实习做的什么内容?遇到什么难点?怎么解决的?
- 对不同设备(PC、手柄、手机)的输入层适配方案有了解吗?
- 你们自研的 Airtest 框架有什么功能?针对不同平台做了哪些适配?项目组下一步的开发方向是什么?
- 说说 Unity 或者 C++ 中常用的数据结构?
- 数组和链表在游戏开发中有哪些应用场景?LRU 机制在游戏开发中有哪些应用场景?
- 你觉得 Lua 设计不好的地方有哪些?table 和 metatable 了解吗?
- Lua 怎么实现面向对象?元方法 index 和 __index 有什么区别?
- 平时怎么用 AI?项目组里 AI 使用情况?
- 未来规划更倾向于走 UE 还是 Unity 客户端开发?为什么?
- 哪里人?希望的 base 地是哪?
《参考解析》
多端输入适配:核心是「把物理输入抽象成游戏动作」,中间加一层映射表,而不是在业务代码里判断设备类型。输入层负责:设备发现与热插拔、原始事件采集(键鼠、手柄的轴与按键、触屏的触摸点与手势)、归一化(把手柄摇杆的模拟量、键盘的离散量、触屏的滑动都映射到统一的轴值)、配置化(同一个动作可以绑多个物理输入,玩家可自定义)、上下文切换(UI 界面 vs 战斗场景的输入屏蔽与优先级)。手柄的难点在死区(deadzone)与曲线、跨平台按键编号不一致;触屏的难点在触摸与鼠标事件的重复触发、虚拟摇杆的手感。
Airtest 这类自动化框架:Airtest 本质是基于图像识别 + 输入注入的 UI 自动化。跨平台适配要做的是抽象「截屏」「点击/滑动」「按键」三类原语,再针对 Android(adb)、iOS(WDA)、Windows(桌面截屏与 SendInput)分别实现。游戏项目里常用它做每日构建的冒烟回归(进游戏、走日常流程、检查关键界面截图)。局限要说清:图像识别对分辨率、语言、动效敏感,维护成本随版本变化上升;所以更好的做法是给关键节点加「可被识别的标记」(特定颜色块、内部调试面板)。
数据结构与场景:数组适合连续访问与批量遍历(顶点缓冲、tile 地图、帧数据),缓存友好;链表适合频繁插入删除且不关心随机访问(对象池的空闲链、事件监听链、技能 buff 列表)。游戏里高频使用的是对象池(避免 GC 抖动)、空间划分结构(四叉树/八叉树/网格,用于碰撞与视野裁剪)、环形缓冲(网络包、输入历史、战斗回放)、堆(A* 的 open 表、定时器)。
LRU 的游戏场景:资源管理是最典型的——贴图、模型、音频按最近使用淘汰,避免内存爆掉;其次是 UI 界面缓存(Tab 页、列表项)、寻路结果缓存(同一对起终点的路径)、以及网络层的数据包缓存。实现上用哈希表 + 双向链表,或者直接用引擎自带的资源引用计数配合一个访问序链表。
Lua 的设计取舍:优点是小、快、易嵌入、和 C 交互方便。被吐槽的点:只有一种数据结构 table(数组和字典混用,边界语义容易搞错)、没有原生的整数类型(5.3 之后有了但生态兼容性参差)、错误处理靠 pcall 且容易吞异常、全局变量默认可见(拼错变量名不报错,是新手最大的坑)、没有真正的私有成员、GC 是增量式但仍有峰值。table 是关联数组,metatable 是给 table 挂「元行为」的表。
Lua 的面向对象与 __index:没有类语法,用 table + metatable 模拟:把方法放在一个 table 里作为「类」,实例的 metatable 的 __index 指向它,访问实例上不存在的字段时就会去类里找。index 和 __index 的区别是:__index 是元方法(metatable 里的一个字段,值是函数或 table),index 只是普通字段名。访问 t.k 时,如果 t 本身没有 k,Lua 读 getmetatable(t).__index,是 table 就继续在它里面找(可链式继承),是函数就调用 __index(t, k)。继承就是让子类 metatable 的 __index 指向父类。另外 rawget 可以绕开元方法直接取。
职业方向题怎么答:不要空谈「都喜欢」。给判断依据:项目里实际用过哪个、哪个生态与自己擅长的(渲染、性能优化、工具链)更匹配、以及目标项目用什么引擎。承认两者的差异(Unity 的 C# 生态与包管理、UE 的 C++ 与蓝图工作流、源码可及性)比说「都能学」有说服力。