最近面试中遇到的一些题目,其中一些答的不满意的和不会答的,在此记录下来,包含Unity3D的、C#和Lua的,以及一些算法题。
Unity3D
如何控制物体的渲染顺序(哪些因素影响到渲染顺序,影响的权重如何)?
影响因素如下:
- 相机深度(Camera depth)
- SortingLayer
- OrderInLayer / SortingOrder
- RenderQueue
- 到相机的距离
如何影响:
- 相机深度影响权重最大,depth值小的会更早绘制;
- RenderQueue小于等于
RenderQueue.GeometryLast
的会比大于RenderQueue.GeometryLast
的先绘制(RenderQueue.GeometryLast
的值是2500); - 若两个对象的RenderQueue同时小于等于
GeometryLast
或同时大于GeometryLast
,则依次依据SortingLayer、OrderInLayer/SortingOrder和RenderQueue权重从大到小排序,即:- 如果SortingLayer不相等,SortingLayer小者更早绘制;
- 如果SortingLayer相等,则OrderInLayer/SortingOrder小者更早绘制(OrderInLayer/SortingOrder取值范围-32768~32767);
- 如果SortingLayer和OrderInLayer/SortingOrder均相等,则RenderQueue小者更早绘制;
- 若两个对象的RenderQueue同时小于等于
GeometryLast
或同时大于GeometryLast
,且SortingLayer、OrderInLayer/SortingOrder和RenderQueue全都相等:- 若RenderQueue小于等于
GeometryLast
,则距离相机近者更早绘制; - 若RenderQueue大于
GeometryLast
,则距离相机远者更早绘制;
- 若RenderQueue小于等于
补充:
MeshRenderer使用的SortingOrder,对于UGUI中的Canvas而言叫做OrderInLayer;
对于Canvas而言,如果同一个Canvas中各个节点的z坐标不同,则会选择各元素的平均位置作为距离相机的位置来参与排序;
UGUI中,Anchors和Pivot的含义和区别?
Anchors:表示RectTransform的左下角和右上角在父节点中的位置,分别对应anchorMin和anchorMax。
Pivot:RectTransform自身旋转和缩放的中心。
RectTransform.anchoredPosition是Pivot相对于Anchor的位置,如果Anchors没有重合,则会根据Pivot的位置对Anchors插值得到anchoredPosition。
C#
List是链表还是数组?
本质是数组。
Dictionary<T,U>是无序还是有序?
无序索引,其实现基于hash table,线程不安全。
如果有序索引可以使用SortedList,线程安全可以使用ConcurrentDictionary。
Lua
Lua中使用table做配表时的一些优化策略?
假如现在有这样的配表:
1 | local hero_config = { |
- 使用字典形式的表保存配置信息时,如果有大量的字段保存的是相同的值,可以增加“默认值”这样一行,配置每行的字段时,如果该值等于默认值,则空缺,通过元表的形式使其从默认行去取值。按照这一思路优化后:
1 | local hero_config = { |
- 使用整数做索引。各行的字段名称单独提出来,在保存字段值时直接使用数组形式的表。读表时根据字段获取到对应值在数组中的索引位置,然后再获取字段的值。按照这一思路优化后:
1 | local hero_config = { |
Lua中table对象包含哪些内容(由什么组成)?
lua的table中包含内容参见lua源码中Table的定义:
1 | typedef struct Table { |
主要包括:
- GC相关的信息:CommonHeader、gclist
- 数组部分的信息:array、sizearray
- hash部分的信息:node、lastfree、lsizenode
- 元表和元方法的信息:metatable、flags
Lua中如何实现面向对象?
项目中lua面向对象的实现很类似云风的方法,主要思路都是把基类设为子类的元表的__index
,并且把类设置为类的实例的元表的__index
。
如果有两个脚本互相require,会出现什么结果?
如果直接两个脚本互相require
,会出现下边的错误:
1 | lua: ./a.lua:2: loop or previous error loading module 'b' |
使用module的方式可以有效避免这样的问题,如分别定义三个lua脚本,并执行main.lua:
1 | -- a.lua |
1 | -- b.lua |
1 | -- main.lua |
将会输出:
1 | will require a |
算法题
拷贝一个链表结构
题目:一个链表结构,链表中的各个Node中,所保存的数据data是对其它Node的引用,可以是引用上游、下游的Node也可能是引用的自己,现在要将整个链表结构拷贝一份,如何操作?
1 | struct Node |
当时的回答(其实后来也没有想出来更好的答案):
借助一个字典保存索引关系,需要对链状结构遍历两遍,时间复杂度O(n)。
- 第一遍遍历时,一边创建新的链表一边向字典写入,key是被复制的原始的Node,value是复制之后新得到的Node;
- 第二遍遍历时,在新的链表中填入data数据,读取对应的原来Node的data指向的Node,从字典中获取其对应的位于新链表中的Node,并在新的链表结构中将data指向该Node。
安卓解锁手势密码,列出所有的可能
题目:解锁手势密码,数字1-9一共9个按键,密码长度为4到9位,每个数字最多出现一次。其中数字2、4、6、8和5有点特殊,连接1-3时,如果之前2没有被选过,那么会在1-3之间插入2,即1-3-2;如果之前已经有过数字2,则直接1-3。数字4、6、8和5也同理。需要列出所有的可能的密码。
当时的解法:使用一个树来保存结果,逐层添加新的数字,每次在后边加入新的元素时,需要判断是否有经过的数字(比如在1的后边填3,如果前边没有出现过2的话,则无法添加3,只能添加2)。感觉思路没有什么问题,但是当时需要在墙上把解题过程用代码写出来,后来写着写着就跪了。
图形学
shader中制作描边特效的方法
对于模型的描边,通常是借助于模板缓冲区,在单独的pass中对顶点做出一些沿法线的偏移来绘制。对于sprite的描边则不得不在片段着色器中实现。各种方法整理了一下,放在这里了。
其它
还有一些问题,是难以回答出来或者完全不知道答案的,以后有时间再慢慢学习和研究:
- 进程间通信的方法
- 帧同步vs状态同步
- 群体单位移动时的算法(避免拥塞)
REFERENCE
https://docs.unity3d.com/Manual/UIBasicLayout.html
https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs