栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

游戏客户端开发面经

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

游戏客户端开发面经

本文记录下客户端开发面试的问题(社招),本来想着有大厂经验,去面试的时候随便准备下就好,结果被疯狂打脸,还是得准备下一些常考问题,虽然平时大概率用不到,还是得准备面试造飞机的准备,
祝大家早日找到好工作~

1、帧同步与状态同步,它们的区别是什么,什么类型的游戏适合帧同步,什么类型的游戏适合状态同步。

答:帧同步中,服务器广播玩家的操作,状态同步,服务器同步玩家的属性;对于玩家数量少,非常需要注重手感的游戏适合帧同步(王者荣耀、魔兽争霸),对与玩家数量多,手感要求没那么高适合用状态同步(qq飞车、荒野行动等);它们的重要区别是战斗的核心逻辑写在哪里,帧同步的战斗逻辑写在客户端,状态同步的战斗逻辑写在服务端;由于玩家的属性比较多,所以状态同步需要发送的数据比较多,而且通常都是采用分批次发送,所以网络延迟会更高,而帧同步只需要转发玩家的操作,延迟比较低。详细的文章可以参考这里

2、map和unordered_map的实现原理。

答:unordered_map使用hash表实现的,效率高,无序;map的底层实现是红黑树,有序,对于红黑树,删除、插入、查找的时间复杂度都是logN。

3、野指针问题,为什么野指针会导致崩溃?

答:指针的释放问题,当两个指针p1,p2同时指向一个地址,其中一个指针p1释放了,再去调用另外一个指针p2,由于该地址已经释放了,属于无效地址,所以为导致出错,又或者一个指针指向栈内的一个地址,当程序出了这个栈的作用域之后,再去访问这个指针指向的地址,就是访问无效地址,导致出错崩溃。

4、右值引用的问题,介绍下右值应用。

答:对于左值右值问题,平时开发可能不怎么会用到这个,但是面试有可能会被问到,在STL中,一般回用到move这个函数,它的作用就是把左值转换为右值,就是一个类型转换函数,在STL库中,经常将右值作参数类型,做浅拷贝,提升新性能,例如STL中Vector的push_back,当您传右值进去的时候,就会作为浅拷贝,eg:

#include
#include
#include


using namespace std;

int main()
{
	vector v;
	string s = "asd";
	v.push_back(s);
	cout << "s = " << s << endl; // 输出 s = asd
	v.push_back(move(s));
	cout << "s = " << s << endl; //输出 s = 

	return 0;
}

此时相当于做了一次浅拷贝,将s的指针指向的值转移到了数组s中,并且将s指向空;另外就是在unique_ptr中也经常会用到move,其目的是保证unique_ptr是指向该地址的唯一指针,这样就可以避免刚开始提到的出现野指针的情况,对于详细的左右值介绍请看这里, 文中提出了一个重要额结论,就是std:move单独使用,是不会提升性能的,它只是一个类型转换函数,需要和其他功能配合使用,才有可能会往性能提升方向靠拢。

5、为什么解释型语言能够热更,编译型得不能热更呢(其实这个问题比较局限)?

答: 对于android手机而言,系统是基于linux开发的,所以可以通过动态连接库加载热更的代码(.so文件)。对于ios手机而言,运行app的时候会为appstore审核过的代码开辟专用的内存空间,而其他app中的数据或者通过代码从线上下载的数据加载的时候会将存放的内存空间定义描述符定义为禁止运行,这样ip寄存器将不能够跳转到该空间,因此这部分代码不能运行;但是为什么解释型的语言又可以呢,例如lua、python,对于这两种语言,在不开启JIT的情况下,是解释运行的,就是直接将代码输入给虚拟机,然后虚拟机边读取边执行,虚拟机这部分代码在包体提交到appstore之后是合法的,所以对于ios来说,并没有新的要执行的代码,只是虚拟机输入的数据有变化而已。当然,当然想其他童鞋说的那样,使用C++/C#解释器仍然是可以热更的。其实使用lua的主要原因是lua的字节码执行比较快,并且模拟器(虚拟机)运行比较稳定,维护足够好。同时lua模拟器足够轻量,方便扩展。也有部分项目组是用IL_RunTime热更,好处是热更用的是C#,不用再加脚本进行开发了,当然可能用的项目不多,需要踩的坑很多,所以大部分项目还是用的C++/C# + 脚本语言(lua、Python)进行开发。

6、C#以及mono的内存回收方式。

答:它们两个都是通过GC方式进行回收的,
对于C#:首先资源分为托管资源和非托管资源,非托管资源需要自己去实现对应的回收函数接口IDispose;托管资源,指的是引用类型的对象,这一部分是需要GC去管理的;GC时从根集遍历,把能找到应引用的和不能找到引用的都标记出来,最后将没有引用的对象清除;GC的算法是采用的分代处理,这样做的目的是为了加快回收速度,减少GC是的卡顿现象,主要分为0、1、2代,每一代都有自己的内存预算,超过预算之后出发GC,GC之后0代中还存活的对象变为1代,依次内推,一般GC的调用机制时系统控制的,当然也可在代码中手动调用GC:GC.Collect();总体思路就是标记+分代处理垃圾对象。
对于mono,采用的方式就比较简单些,mono的内存分为空闲内存+已分配的内存,当需要分配内存时,检查空闲内存是否够,如果够进行分配,如果不够,进行GC,GC完不够,向操作系统申请内存;其中mono内存有个特点就是向操作系统申请的内存是不会还给操作系统的;为了避免GC峰值造成的卡顿,unity也可以设置分步GC机制,也叫做步进式GC。

7、什么时虚拟内存

答:虚拟内存是一种计算机系统内存管理技术,它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换;例如我们在Windows系统中,通常将虚拟内存设置为真是内存的1.5倍。由于物理内存有限,对于一些大型程序(游戏),需要占用的空间就比较大,此时就可以把一些资源放在磁盘中进行IO,实质上就是为了方便管理程序的地址以及利用磁盘的部分空间增大内存。

8、C++多态原理,虚表及其重载的实现原理。

答:C++多态分为静态联编和动态联编;静态联编就是指在编译的时候知道调用什么函数,例如函数的重载(参数类型、参数个数不同),动态联编是需要在程序具体执行的时候才能确定函数的调用,例如父类指针指向子类指针,父类存在虚函数;重载的原理就是编译器在编译的时候,会根据函数的参数类型将对应的函数重新命名,这一样就保证了函数的调用唯一性;对于虚函数,是通过虚表实现的,每一个存在虚函数的类或者其父类存在虚函数,在其实例中都会有一个虚表,这个虚表记录了其指向的虚函数的调用地址,如果是多继承,就会有多个虚表(一般很少用到)。如果调用的是虚函数,那么就去看这个类指针实际指向的是谁,如果不是虚函数,调用的就是这个指针类型对应的函数。

9、二维平面问题及其拓展 ----- 点到直线的距离,点是否在在扇形内,怎么判断扇形和圆相交。

直线的距离:直线上两个点A、B, 直线外的点P,通过向量法做,求出向量AP在直线AB上的投影向量AM,然后通过向量减法求出点P到线最近点M的向量:PM = AP - AM, C#代码如下:

float getPointLineDis(float2 p, float2 a, float2 b)
{
	float2 v1 = p- a; // AP
	float2 v2 = b- a;  
	float2 v3 = dot(v1,v2) * v2 / pow(length(v2), 2); // AM
	return length(v1 - v3); // length(PM)
}

判断点是否在扇形内部: 首先计算扇形圆心O和所求点P的距离d,如果大于扇形半径,则一定不在扇形内部,其次,计算点P与圆心的连线向量与扇形角平分线的夹角,这个加角一定是小于扇形角度的一半,当然前提是扇形的角度theta小于180,当大于180时可以转换为求360 - theta;

判断扇形和圆是否相交: 通过求分离轴的方式,判断扇形是否和圆相交,大概方式就是通过求圆心到扇形两条边(线段)的最短距离以及扇形圆心到所求圆的圆心的距离可以判断,简化可以通过对称性以及坐标转化求的,具体的详细算法可以参考这里

10、UI中图集是什么,有什么作用?

答:UI图集是对某些粒度UI所用所用到的图片资源的打包,使之可以通过一次drallcall画出来;在unity中,可以通过把在一个canvas下的图片资源打包为图集,使之dp减少;当然,对于UI合批的条件是至少要满足材质和贴图一致才能合批,ui还需要满足其他的一些条件,例如在对应的hierarchy视图下面层级顺序有可能会一项到合批。总之图集的作用是减少dp,图集的粒度(最小单位)一般是以画布canvas为单位。

11、物理碰撞计算中,如果速度非常快,那么相邻两帧有时候就会出现穿插而导致没有检测到碰撞,这种情况怎么处理?

答:可以通过保留上一帧的位置信息,计算是否碰撞,例如:一个子弹飞向一面墙,可以通过子弹这一帧的位置和上一帧的位置连线是否与墙相交,相交则发生了碰撞,没有相交则没有发生碰撞。如果是两个运动很快的物体,则先利用相邻前后帧位置计算出线段,判断线段是否相交,如果相交,算出交点,然后再用出发点到交点的距离以及运动速度和碰撞物体体积进行计算。

12、深拷贝和浅拷贝

答:对于指针而言,浅拷贝是指连个指针指向同一个地址,深拷贝是指在内存空间中新开辟一个地址,并且复制需要拷贝的内容;在其它语言中也会遇到这种问题,例如Python中的copy与deeppeek,当用copy.copy时,会出现修改复制出来的对象,最后溅射到原来对象的情况,原理和C++的深浅拷贝一样。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/953702.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号