面试题库
C#相关
1. 装箱和拆箱是指什么?
解析查看
1.装箱——把栈中内容迁移到堆中去(值转引用) 2.装箱——把堆中内容迁移到栈中去(引用转值) 3.如果问到值类型和引用类型同样,要引申这个
2. 值和引用类型在变量赋值时的区别是什么?
解析查看
1.值类型在赋值时只是把数据值复制过去,值类型数据值具有独立性 2.引用类型在赋值时复制的是引用(地址),一这意味着两个变量指向同一个对象,对其中一个变量进行修改也会影响到另外一个变量。
3. 委托和事件在使用上的区别是什么?
解析查看
1.委托可以在外部调用,但是事件不能在外部直接调用,只能通过+=或者-=来取消/订阅事件 2.想要调用必须在类内部再封装一个方法来调用事件 3.事件不能在外部直接赋值,但是委托可以
代码查看
public class Publisher { public event NotificationHandler Notify; // 封装为事件 public void SendMessage(string msg) { Notify?.Invoke(msg); // 安全触发事件 } } // 外部代码只能订阅/取消订阅 var publisher = new Publisher(); publisher.Notify += (msg) => { Console.WriteLine(msg); }; publisher.Notify -= ...; // 无法直接清空事件
4. 有两个接口IA和IB,他们中有一个同名方法Test()一个类同时继承这两个接口,应该如何处理他们的同名方法?
解析查看
显示实现接口 IA.Test() IB.Test()
5. 请说明C#中的List是如何扩容的
解析查看
List的底层实现是使用数组 当List容量不足的时候,他会重新再开辟一个大内存,数组大小通常会扩大一倍,并将现有元素复制到新数组
6.请说说你认为C#中 == 和 Equals 的区别是什么?
解析查看
1. == 是运算符,Equals是万物之父Object中的虚方法,子类可以重写 2.Equals 一般再子类中重写后用于比较两个对象中的内容是否相同 ==在没有运算符重载的前提下时 引用类型用于比较地址;值类型用于比较值是否相同 3.运算效率不同,一般Equals没有==效率高,因为一般Equals比较的内容比==读多
7. 浅拷贝和深拷贝的区别?
解析查看
浅拷贝: 只是复制对象的引用地址 深拷贝: 计算机在内存上重新开辟了一个空间,把对象的引用地址和值圈复制过来,两个对象是独立存在的。 举例: 比如引用对象A和引用对象B 让A = B,就是浅拷贝,此时A、B的引用地址相同,改A中内容,B也变 如果想要深拷贝,简单处理就是new(包括对象中的成员)
8. 上面两种获10000个数的方式,哪种效率更高?为什么
解析查看
方式2的效率更高 因为List本质是数组,我们通过Add往List中添加元素时,会不断的触发扩容 扩容会带来内存和性能上的消耗 内存方面:每次扩容会产生垃圾,还会造成GC的触发 性能方面:每次扩容会进行“搬家”(老数组中内容存入新数组中)
9. 请说出以上代码:1.A处和B处谁先打印? 2. A、B出打印的i值分别是多少?
解析查看
答案: 1.B处先打印,A处后打印 2.A处 i = 10,B处 i = 11 解析: try → catch(如果有异常) → finally
10.泛型的约束有哪几种?
解析查看
答案: 1.值类型约束 T:struct 2.引用类型约束 T:class 3.公共无参构造约束 T:new() 4.类约束 T:类名 5.接口约束 T:接口名 6.另一个泛型约束 T:U
11 . 什么是闭包?可以举例说明
解析查看
闭包是指有权访问另一个函数作用域中的变量的函数 所以闭包一般都是指的一个函数 创建这种特殊闭包函数的方式往往是在一个函数中创建另一个函数
12 . 内存泄漏指什么?常见的内存泄漏有哪些?
解析查看
内存泄漏是指某些对象废弃了,不想使用了,但是不能被GC垃圾回收,一直占用者内存 常见的内存泄漏有: 1.静态引用 2.不使用的引用对象没有置null,一直被引用 3.文件操作时,没有使用using或者没有进行Dispose() 4.委托或事件注册后没有解除注册(有加就有减)
13. 序列化是什么?常见的序列化方式有哪些?什么时候我们会用到序列化?
解析查看
答案: 序列化是将程序中数据对象转换为可以存储或传输的形式 的过程。 举例: 比如我们常见的序列化方式 xml、Json、2进制等。就是将内存中的数据按照我们自己定义的规则进行序列化,序列化之后就可以用于存储和传输,当读取和接受数据时,只需要按照对应规则进行反序列化便可得到原始数据 所谓的存储读取和传输接受,其实一般指的就是数据持久化和网络通讯 所以我们经常会在这两块知识点看到序列化反序列化这两个关键词
14. 请说明以下三者的区别
string str = null
string str = “”
string str = string.Empty
解析查看
答案: str = null 在堆中没有分配内存地址 str = "" 和 string.Empty 一样都是在堆内存中分配了空间,里面存储的是空字符串 而string.Empty是一个静态只读变量
15. C#重载运算符,重载 == 和 != 以及 万物之父Object基类中的虚方法 virtual bool Equals(Object obj)对于我们的意义是什么?
解析查看
1.重载== 和!= 因为默认== 和!=对于引用类型是判断对象引用是否相等 重写这个可以自定义判断对象里面的值,而不是引用 2.重载 Equals 如果想保留判断引用地址(保留==)相等,就重写Equals
16.在开发时,对string和StringBuilder我们应该如何选择
解析查看
答案: string在每次拼接时都会产生垃圾 而StringBuilder在拼接时,是在原空间中进行修改,不会产生垃圾,会自动帮助我们扩容 所以当字符串需要频繁修改拼接时,我们使用StringBuilder
17. 请简要说明.Net跨语言原理
解析查看
答案: .Net制定了了CLI公共语言基础结构的规则 只要是按照该规则设计的语言在进行.Net相关开发时 编译器会将源代码(C#、VB等等)编译为CIL通用中间代码。 也就是说不管什么语言进行开发,最终都会统一规范变为中间代码 最终通过CLR(公共语言运行时或者称为.Net虚拟)将中间代码翻译为对应操作系统的原生代码(机器码) 在操作系统(Windows)上运行
18. 请简要说明.Net跨平台原理
解析查看
答案: 由于.Net Framework中利用CLI和CLR实现了跨语言,CLR主要起到一个翻译、运行、管理中间代码的作用 .Net Core和Mono就是利用了CLR的这一特点,为不同操作系统实现对应CLR(公共语言运行时或.Net虚拟机) 那么不同操作系统对应的CLR就会将IL中间代码翻译为对应系统可以执行的原生代码(机器码) 达到跨平台的目的
19. 数组和链表的区别是什么?
解析查看
答案: 1.存储结构不同 数组是顺序存储结构,在内存中是连续存储的 链表是链式存储结构,在内存中是非连续存储的 ----------------------------------------------------------- 2.访问效率不同 数组由于是顺序存储,通过下标访问,访问效率高 链表由于是非连续存储,我们想要获取其中某一元素,需要从头或尾遍历,效率低 ------------------------------------------------------------ 3.插入、删除效率不同 数组由于是顺序存储,在插入和删除时,需要整体移动数组中的大部分元素,效率低 链表由于是链式存储,在插入和删除时,效率高 --------------------------------------------------------------- 4.越界问题 数组由于是顺序存储,声明时容量是固定的,如果不处理扩容逻辑,存在越界风险 链表由于是链式存储,无越界风险
20. C# 中的Action和Func是什么?Unity 中的UnityAction是什么?他们有什么区别?
解析查看
答案: Action和Func是System命名空间下 C#为我们提供的两个写好的委托 Action本身是一个无参无返回值的委托 对应的Action<>泛型委托支持最多16个参数 ------------------------------------------- Func本身是一个无参有返回值的委托 对应的Func<>泛型委托支持最多16个参数,并且有返回值 ------------------------------------------------ UnityAction是UnityEngine.Events命名空间下 Unity为我们提供的写好的委托 UnityAction本身是一个无参无返回值的委托 对应的UnityAction<>泛型委托支持最多4个参数
21. (结构体中的引用类型)请问最终得到打印结果是什么??
解析查看
答案: 0-Alice-7 考点: 1.值和引用的区别 2.特殊引用类型string 3.结构体中的引用成员
22. C#中如何让自定义容器类能够使用for循环遍历?(通过 类对象[索引] 的形式遍历)
解析查看
答案: 通过在类中实现索引器实现
23. C#中如何让自定义容器类能够使用foreach循环遍历?
解析查看
答案: 通过为该类实现迭代器可以让其使用foreach遍历 传统方式: 继承IEnumerator、IEnumerable两个接口 实现其中的 1.GetEnumerator方法 2.Current属性 3.MoveNext方法 语法糖方式: 利用yield return语法糖,实现GetEnumerator方法即可完成迭代器的实现
24. C#中接口的作用是什么?说说你的理解
解析查看
答案: 用于建立行为的继承关系,而不是对象 不同对象,有相同行为时,我们可以利用接口对不同对象的行为进行整合(里氏替换原则)
25. 请问以上这三行代码,运行后,在堆上会分配几个“房间”
解析查看
答案: 2个房间 "123"一个房间 "1234"一个房间
26. C#中如何让一个类不能再被其他类所继承?
解析查看
答案: 使用密封关键字sealed修饰该类
27. C#中元组对于我们的作用是什么?
解析查看
答案: 可以在不用写数据结构类的情况下 利用元组处理多返回值,或者临时数据的集合
28. C#中使用泛型的好处是什么?
解析查看
答案: 1.可以为不同类型对象的相同行为进行通用处理,提升代码复用率 2.避免装箱拆箱,提升性能
29. 请说明Thread、ThreadPool、Task分别是什么?并简单说明彼此的区别
解析查看
答案: Thread是线程,可以使用他开启线程处理复杂逻辑,避免主线程卡顿 ThreadPool是线程池,他是C#为线程实现的缓存池,主要用于减少线程的创建,减少GC触发 Task是任务,他是基于线程池的优化,让我们可以更方便的控制线程 ThreadPool无法控制线程池中线程的执行顺序,并且无法得到当前线程的执行状态。Task的优化的点就在这
30.请简述GC(垃圾回收)产生的原因,并至少说出避免GC发生的三种方式?
解析查看
GC产生的原因是,当我们不使用的堆内存占用达到一个上限时,将会使用算法对垃圾进行回收,不同的语言垃圾回收机制是不一样的 Unity:BoehmGC(贝母垃圾回收器,这个主要是IL2CPP下的算法,现在大部分游戏的编译方法都是这个) 贝母会调用一个第三方库,那个库在进行垃圾回收的时候,会停止正在运行的程序,在完成所有工作之后才会执行。 所以就会导致程序可能突然就会延迟执行,游戏也就会掉帧。所以为了优化,贝母把CG的工作平均分配给n帧 就是怕所有GC都集中在几帧上,出现严重掉帧。 他主要是渐进式的GC模式,把GC的工作分配到多个帧上来减少GC尖峰破坏动画的流畅性。 Lua采用基于标记-清除(Mark-and-Sweep)的垃圾回收机制,核心通过三色增量标记算法实现低开销回收。标记阶段从根对象出发,递归标记存活对象(黑色),未访问对象为白色,灰色对象表示待处理;通过分步执行(如每次执行一步GC并交还控制权),避免长时间停顿,适合实时性要求高的场景(如游戏脚本)。但标记-清除不移动对象,易产生内存碎片,需依赖内部碎片整理或手动触发紧凑阶段缓解。 ------------------------------------------ C#采用分代回收(Generational GC)策略,将对象分为三代(0代、1代、2代),年轻代(0代)使用复制算法快速回收短生命周期对象,老年代(2代)通过标记-整理(Mark-and-Compact)消除碎片并压缩内存。 具体如下: https://www.cnblogs.com/zblade/p/11357203.html -------------------------------------------------------------- 避免GC的方式: 1.避免大量对象的重复创建,尽量复用对象(对象池) 2.用StringBuilder替换String,避免字符串拼接时产生的垃圾 3.尽量避免拆装箱 4.公共对象用静态声明 2.装箱——把堆中内容迁移到栈中去(引用转值) 3.如果问到值类型和引用类型同样,要引申这个
31. 如果我们想为Unity中的Transform类添加一个自定义的方法,应该如何处理?
解析查看
答案: 使用C#扩展方法 1.扩展方法需包含在 static 修饰类中。 2.扩展实现需是静态形式。 3.扩展方法第一个参数 前缀为 this , 表示需要扩展类对象,从第二个参数开始,为扩展方法参数列表。
32. 请说出using关键字的两个作用
解析查看
答案: 1.引入命名空间 2.安全使用引用对象
33. C#中Dictionary不支持相同键存储如果想要一个键对应多个值如何处理?
解析查看
答案: 一个键,对于一个对象容器 比如:<键,泛型列表>,<键,数组>,<键,栈>
34. 请问上面代码的最终打印结果是什么?为什么?
解析查看
答案: 全是10 当委托最终执行时,他们使用的i,都是for循环中声明的i,此时的i已经变成了10
35.上题中的代码,如果我们希望打印出0~9,应该如何修改代码?
36. 内存中,堆和栈的区别是什么?
解析查看
答案: 堆和栈是操作系统堆进程占用的内存空间的两种管理方式 ----------------------------------------------- 栈:由操作系统自动分配释放,存放函数的参数值,局部变量值,栈中数据的生命周期随着函数的执行完成而结束 ------------------------------------------------------- 堆:一般由程序员分配释放,如果开发人员不释放,程序结束时由操作系统回收 (在C#中 托管堆内存 会由 C#帮助我们管理,存在GC垃圾回收机制) 2.装箱——把堆中内容迁移到栈中去(引用转值)
37.TCP协议和UDP协议的区别
解析查看
答案: 连接方面:TCP面向连接,UDP无连接 是否可靠:TCP可靠(无差错、不丢失、不重复、按顺序),UDP不可靠 传输效率:TCP相对UDP较低 连接对象:TCP一对一,UDP n对n
38
. TCP协议的可靠性是如何达到的?
解析查看
答案: TCP协议是通过 检验和、确认应答信号、重发机制、连接管理、流量控制、拥塞控制等手段达到可靠的 具体的一些理论知识,可以浏览该文章 http://www.360doc.com/content/22/1111/20/78411425_1055522293.shtml
39. 内存抖动指什么?如何避免内存抖动
解析查看
答案: 内存抖动指短时间内有大量的对象被创建或者被回收的现象 频繁的内存抖动会造成 GC 频繁运行,造成卡顿 避免方式: 对象池 享元模式 等
40. 文件中保存了文本信息,但是打开后却是乱码,一般是什么原因造成的?
解析查看
答案: 序列化和反序列化字符串时使用的编码格式不统一 比如:序列化时使用UTF—8,反序列化时使用Unicode
41. C#中new关键字的作用(至少说出3种)
解析查看
答案: 1. 创建新对象 2. 子类函数声明时加上new关键字,可以隐藏掉父类方法 3. 泛型约束中使用new关键词,表示需要无参构造
42. 同步方法和异步方法的区别是什么?异步编程是什么意思?
对于我们来说,什么时候需要使用异步编程?(至少说出3种)
解析查看
答案: 同步方法: 当一个方法被调用时,调用者需要等待该方法执行完毕后返回才能继续执行 -------------------------------------------------------------- 异步方法: 当一个方法被调用时立即返回,并获取一个线程执行该方法内部的逻辑,调用者不用等待该方法执行完毕 ------------------------------------------------------------------------------ 异步编程: 在日常开发时把一些不需要立即得到结果且耗时的逻辑设置为异步执行,这样可以提高程序的运行效率,避免由于复杂逻辑带来的的线程阻塞 ------------------------------------------------------------- 什么时候需要使用异步编程: 1.复杂逻辑计算时,比如寻路算法等 2.网络下载、网路通讯 3.资源加载时 等
43. 回调函数指什么?一般在什么时候使用?(至少说出3种使用场景)
解析查看
答案: 回调函数指在程序设计中,将一个函数作为参数传递给另一个函数,并在另一个函数执行完毕后被调用的函数,在C#中,一般以委托形式出现 ------------------------------------------------------------------ 什么时候使用: 1.异步编程:异步逻辑执行完毕后,再执行回调函数 2.事件中心 3.UI界面中的空间逻辑回调,比如按钮点击 等等
44. 如何用一个int变量,记录32种状态?(注意:状态可以并存)
解析查看
答案: int在C#中占4个字节,共32位 我们可以按位记录状态,每一位代表一个状态,1为存在,0为不存在
45. csharp里面有一个常用的接口叫做IDispose,它的作用是?
解析查看
答案: IDispose接口是 C# 中用于手动释放资源的机制 通过显式调用 Dispose() 方法来实现资源的释放,避免资源泄漏和浪费 它允许对象在不再需要时显式地释放资源,而不依赖于垃圾回收器的自动内存管理
46. 上一题追问:csharp有垃圾回收机制,把一个对象设置为空的时候,系统会帮我们回收,它和接口IDispose有什么关系吗?为什么会存在两个做析构事情的东西呢?
解析查看
答案: C#中的垃圾回收机制,只会回收托管堆上分配的对象。 对于非托管资源以及其它需要显示释放的资源,垃圾回收是无法自动处理的,因为这些资源不属于托管堆,因此垃圾回收器无法自动识别和回收。这种情况下我们就需要显示的手动释放这些资源了。 而IDispose接口就提供了一种通用的机制来进行资源清理,主要用于释放非托管资源。 ---------------------------------------------------------------- 非托管资源: 1.文件句柄,在操作系统中打开的文件等 2.数据库链接,与数据库服务器简历的链接 3.网络链接:比如Socket的连接对象 4.unsafe关键词中使用的指针 等等
47. C#中哪些变量类型是值类型(至少说出13种),哪些是引用该类型(至少说出5种)
解析查看
答案: 值类型: int、float、double、byte、short、long、uint、ulong、uint、sbyte、decimal、char、bool、自定义结构体 ---------------------------------------------------------------------------- 引用类型: 自定义类、数组、字符串、委托、接口、Object
48. 在自定义类中声明的成员变量,类型为int,该int变量存储在栈上还是堆上?在自定义结构体中声明的成员变量,类型为string,该string变量存储在栈上还是堆上
?
解析查看
答案: 1.堆上 自定义类中声明的成员变量,无论什么类型,都会存储在堆上。 类(引用类型)的实例本身存储在堆上,而成员变量也存储在该实例所指向的内存区域(堆上) --------------------------------------------------------------------------- 2.堆上 在结构体变量中的引用类型成员实际上会存储在堆上。虽然结构体本身是值类型,存储在栈上,但是如果结构体的成员包含引用类型,那么引用类型的对象会存储在堆上,而结构体的实例内部会包含对这些堆上对象的引用。
49.C#中在什么情况下会选择使用接口,什么情况下会选择使用抽象类?
解析查看
答案: 接口: 1.不同对象的共同行为 2.需要多继承时 --------------------------------------------------- 抽象类: 1.同类对象的共同行为 2.共享成员变量
50. C#中什么是托管内存和非托管内存?
解析查看
答案: 托管内存: 托管内存是由.NET运行时(CLR,Common Language Runtime)进行管理的内存 C#中大部分对象都是托管内存,它们的内存分配、回收和资源管理都由CLR负责 。CLR提供了自动垃圾回收机制(GC),可以自动检测不再使用的对象并释放它们占用的内存,从而避免了内存泄漏问题。 -------- 比如我们平时声明的引用类型的变量都属于托管内存 -------------------------------------------------------------------------- 非托管内存: 非托管内存是由应用程序自己负责管理的内存,它通常是通过调用本机API或与外部系统进行交互时使用的。非托管内存可能不受CLR的管理,这意味着它不会受到垃圾回收的影响。但是,这也意味着开发人员需要自己负责内存的分配和释放,否则可能会导致内存泄漏或者访问无效内存的问题。 ---------------- 比如我们平时声明的 unsafe语句块中的指针成员,数据库链接对象,Socket通讯对象,文件流等对象都存在非托管内存,需要我们自己释放
51. 请问什么是线程安全?如何在C#中实现线程安全操作?
解析查看
答案: 什么是线程安全? 是指在多线程环境中,对共享资源(如数据、变量、对象等)进行操作时,不会产生不正确的结果或不可预期的行为。在多线程程序中,由于多个线程可以同时访问和修改共享资源,如果没有适当的同步机制,就可能会导致数据损坏、竞态条件(Race Condition)和其他错误 --------------------------------------- 数据损坏:数据损坏是指数据在存储、传输或处理过程中发生错误,导致数据的内容或结构出现不一致、不正确或不完整的情况 ------------------------------------------ 竞态条件:多个线程试图同时访问和修改共享资源(如变量、数据结构、文件等),从而导致程序的行为出现不确定性或不正确的结果。竞态条件可能会导致程序的运行结果与预期不符,产生难以预测和复现的错误 --------------------------------------- 如何在C#中实现线程安全操作? 使用锁(Lock)、异步编程(async、await)等方式
52. C#中属性(Property)和字段(Field)的区别是什么?
解析查看
答案: 属性一般可以用来封装字段 ----------------------------------------- 属性相对字段来说,属性具有封装性,允许对字段进行封装,提供更多的控制和逻辑。 相比直接访问字段来说,属性允许我们在字段访问的过程汇总添加验证、计算等逻辑 ------------------------------------------ 属性还可以在其中对set和get设置不同的访问级别,使得字段的读取和写入可以收到更精细的控制
53. 请解释一下C#中的异步编程模型(Async/Await),它是用来做什么的?
解析查看
答案: C#中的异步编程模型是一种用于处理并发任务的技术 允许在执行异步操作时,让程序继续执行其他任务,而不会阻塞主线程。这对于处理诸如网络请求、文件读写、长时间计算等耗时操作非常有用
54. 抽象类中的虚函数和抽象函数,是否必须在子类中重写?
解析查看
答案: 子类中 虚函数的重写是可选的,当需要在子类中修改逻辑时可以选择重写 抽象函数必须重写
55. 当我们使用里式替换原则,用父类容器装载子类对象时我们通过该父类容器调用其中的一个虚函数,执行的逻辑是父类中的还是子类中的逻辑呢?
1
2
3
4
5
6 Father Eat
Son :Father
Son Eat
Father f = new Son();
f.Eat();
解析查看
答案: 1.若子类没有重写虚函数,那么执行的逻辑会是父类中的逻辑 2.若子类重写了虚函数,那么执行的逻辑会是子类中的逻辑(是否执行父类中逻辑,主要看子类重写时是否利用了base执行父类逻辑) vob
56. 我们在游戏开发时,想要获取系统时间,应该如何获取?
解析查看
答案: 利用C#中提供的DateTime类(System命名空间中) DateTime中的 DateTime.Now 静态成员 可以获取到当前的系统时间
57. C#使用var声明临时变量相比用指定变量类型声明临时变量,在最终执行时是否会增加性能消耗?
解析查看
答案: 不会,var在编译时会被推断为正确的类型,所以在运行时不会引入额外的性能开销。 相当于在编译阶段var就会被翻译为指定的类型。 ---------------------------------------------- var声明临时变量只会影响代码的可读性和可维护性(双刃剑)
58. 我们知道,上述代码b改为20后,a还是10。(值类型的特点)我们是否可以简单修改以上代码,让b变成20后,a跟着一起变。
请说出修改方案
1
2
3 int a = 10;
int b = a;
b = 20;
解析查看
答案: C#7中加入了ref的新用法,使用ref修饰临时变量和函数返回值,可以让赋值变为引用传递 Unity 2018.3及其以上支持C# 7 --------------------------------------------- int a = 10; ref int b =ref a; b = 20;
59. 我们可以使用C#中的迭代器来做什么?至少说出两点
解析查看
答案: 1.foreach 2.协同程序
60. 面向对象的七大原则分别是什么?并选择其中一个原则说明它的意思
解析查看
答案: 单一职责原则: 一个类只处理自己应该处理的内容,不应该啥都写在一起 -------------------------------------------- 开闭原则: 对拓展开放,对修改封闭。新加功能尽量是加处理而不是改代码 ------------------------------------------------ 里氏替换原则: 任何地方子类都能替代父类,父类容器装子类 ------------------------------------------------- 依赖倒转原则: 不要依赖具体的实现,要依赖抽象(接口) ------------------------------------------------- 迪米特法则: 又称最少知识原则,一个类要尽量减少对别的类的了解,尽量少用别的类和自己关联 ------------------------------------------------------- 接口隔离原则: 不应该强迫别人依赖他们不需要使用的方法 一个接口不需要提供太多的行为,一个接口应该尽量只提供一个对外的功能,让别人去选择需要实现什么样的行为,而不是把所有的行为都封装到一个接口当中 ----------------------------------------------------------------- 合成复用原则: 尽量使用对象组合,而不是继承来达到复用的目的 继承关系是强耦合,组合关系是低耦合 除非设计上需要继承,否则尽量用组合复用的形式
61. 为什么单例模式在游戏开发中很常用?至少说出3个优点
解析查看
答案: 1.全局数据共享 2.确保唯一性 3.方便管理资源 4.方便管理对象 5.访问简单化 6.便于扩展 等等
62. 工厂模式在游戏开发中一般用来做什么?
解析查看
答案: 对象实例化 对于需要频繁创建的对象,比如角色、敌人、道具、特效、音效等等,工厂模式可以将实例化逻辑封装到一个工厂类中,提供统一方法给外部调用
63. 观察者设计模式在游戏开发中有什么作用?
解析查看
答案: 它可以让游戏系统之间松耦合,提高代码的可维护性和拓展性 比如在成就系统、任务系统中,我们可以利用观察者模式在不同系统中进行事件监听和分发 ----------------------------------------------------- 一般都是建立的一对多的依赖关系,当一个对象状态改变时,依赖(监听)它的对象会得到通知进行逻辑执行(委托或事件执行)
64. C#中如何在函数中返回多个返回值?(至少说出3种方法)
解析查看
答案: 1.数组等容器 2.结构体或类自定义数据结构 3.out参数 4.元组
65. 在游戏开发中,我们可以利用数据结构中的树参与哪些功能的制作?(至少说出3点)
解析查看
答案: 1.场景中对象的关系,比如Unity中场景上的对象通过树形结构建立父子关系 可以方便我们查找对象,确定对象的层级关系等等 -------------------------------------------------------- 2.行为树AI -------------------------------------------------------- 3.资源管理,资源之间的层级结构可以靠树建立 -------------------------------------------------------- 4.技能系统,比如技能树的表现 -------------------------------------------------------- 5.随机地图生成,比如“肉鸽”游戏中的随机地图,地图中的关系可以用树建立 -------------------------------------------------------- 6.任务系统,比如游戏中的任务面板中的任务清单 -------------------------------------------------------- 7.碰撞检测,碰撞检测的底层一般会采用四叉树或八叉树来加速碰撞检测 -------------------------------------------------------- 等等 2.装箱——把堆中内容迁移到栈中去(引用转值) 3.如果问到值类型和引用类型同样,要引申这个
66
. 父类中定义了一个静态成员属性,有两个子类都继承该父类
请问打印结果是什么?为什么?
解析查看
30 30 30 答案: 因为静态成员属于类而不是实例
67. 泛型父类中定义了一个静态成员属性,有两个子类都继承该泛型父类
请问打印结果是什么?为什么??
解析查看
0,20,20,30 答案: 因为静态成员属于类而不是实例 泛型T的变化,会让父类"类型"变化 不同类型的泛型实例,它们的静态成员 static_I 是分别存在的,不会相互影响
68. 使用C#制作游戏存档功能,请问有几种做法?(至少说出三种)
解析查看
答案: xml json 2进制 自定义文档结构 数据库 等等
69. C#中是否可以通过反射获取到类内部的私有成员?
解析查看
答案: 可以 在获取成员的相关方法中,可以通过传入参数,指定获取非公共的成员 ---------------------------------------------------- 其中的关键知识是,利用BindingFlags 枚举 常用枚举类型 Public (BindingFlags.Public): 包括公共成员。 NonPublic (BindingFlags.NonPublic): 包括非公共成员。 Instance (BindingFlags.Instance): 包括实例成员。 Static (BindingFlags.Static): 包括静态成员。 FlattenHierarchy (BindingFlags.FlattenHierarchy): 在层次结构中查找成员,包括继承的成员。 IgnoreCase (BindingFlags.IgnoreCase): 忽略成员名称的大小写 ------------------------------------------------------------ BindingFlags.NonPublic | BindingFlags.Instance
70.
在制作游戏存档功能时C#中反射主要可以发挥出哪些作用(至少说出三点)
解析查看
答案: 1.序列化时:动态获取数据结构类信息,可以动态获取字段用于存储 2.反序列化时:可以通过反射实例化对象,写入数据 3.结构发生变化时:我们可以利用反射机制进行判断,多的数据抛弃,少的数据自定义初始化
71(坑). 请问上面这段代码的打印结果是多少?
1
2
3 float f = 10.4;
int i = f / 2;
Console.WriteLine(i);
解析查看
答案: 会报错 代码片段中存在两个错误 应该改为 1.float f = 10.4f 2.int i = (int)(f/2)
72. 在C#当中,我们想要用一个字典容器存储场景中的所有敌对对象。比如 怪物、Boss、可被攻击的场景物件等等。应该如何存储
解析查看
答案: 在父类(或接口)写一个唯一的实例ID,用来当键 值就用里式替换原则 父类容器装载子类对象 --------------------- 在父类里面可以再存储类型,然后通过这个就可以as出来
73. 在C#中,给你一个数字,表示有n秒请问如何在不改变这个数字的前提下
将它转换成格式为 __ 小时__ 分__ 秒
解析查看
答案: int h = n / 3600; int m = (n % 3600) / 60; int s = n % 60;
74(坑)
. 请问执行以上代码后,List中还存在哪些内容?
解析查看
答案: 2 4 6 8 10 第1次:i = 0 移除了 1,后面往前放 2,3,4,5,6,7,8,9,10 第2次:i = 1 移除了 3,后面往前放 2,4,5,6,7,8,9,10 第3次:i = 2 移除了 5,后面往前放 2,4,6,7,8,9,10 第4次:i = 3 移除了 7,后面往前放 2,4,6,8,9,10 第5次:i = 4 移除了 9,后面往前放 2,4,6,8,10 结束 注意: 调用RemoveAt删除项时,将重新编号列表中的其余项以替换已删除的项。例如,如果删除索引3处的项,则索引4处的项将移动到第3个位置。此外,列表中的Count属性表示项数将减少1。 -------------------------------------------------------------------------------------------- 这个方法是O(n)操作,其中n是从索引位置到列表末尾的元素数量。
75. 假设游戏中有一个怪物管理器管理所有怪物。那么在开发时,为了避免内存泄漏,我们需要注意什么?
解析查看
答案: 当某一怪物真正需要移除时 我们需要清除怪物管理器对该怪物的引用
76. 重载和重写的区别
解析查看
1.位置不同:重载在同类中,重写在父子类中 2.定义方式不同:重载方法名相同参数列表不同,重写方法名和参数列表都相同 3.调用方式不同:重载使用相同对象以不同参数调用,重写是不同对象相同参数调用
77. ArrayList和List的主要区别
解析查看
最大的区别就是ArrayList不支持泛型,List支持泛型 这就会导致一系列的问题。 由于ArrayList不带泛型 会导致数据类型丢失因为都会转换为Object 并且还会导致拆装箱。
78. LinkedList 和List的区别
解析查看
LinkedList底层是链表,List是数组 因为List添加或删除元素会重写分配内存,所以插入或删除肯会导致性能下降。 List主要适用于随机访问元素的场景。 LinkedList因为是链表 插入删除元素的时候 只需要 调整节点指针内配一块内存即可 但是访问某一元素时,要从头节点开始遍历。
79.
请描述Interface(接口)与抽象类之间的不同
解析查看
1.接口不是类,不能实例化。抽象类可以间接实例化 2.接口是完全抽象,抽象类是部分抽象 3.接口可以多继承,抽象类只能单继承 ----------------------------------------------------------- 1. 接口不是类,不能实例化。抽象类可以间接实例化 接口(Interface): 接口是纯抽象的契约,只定义方法/属性/事件的签名(如 void DoSomething()),不包含任何实现。 不能直接实例化:new IInterface() 是非法的。 使用方式:通过类实现接口(class MyClass : IInterface),然后实例化该类。 抽象类(Abstract Class): 抽象类可以包含已实现的方法和抽象方法(如 abstract void DoSomething())。 不能直接实例化:new AbstractClass() 是非法的。 间接实例化:通过继承抽象类的子类实例化(class MyClass : AbstractClass → new MyClass())。 ---------------------------------------------------------------- 2.接口是完全抽象,抽象类是部分抽象 接口: 在C# 8.0之前,接口所有成员都是抽象的(无实现)。 C# 8.0+ 支持默认实现(如 void DoSomething() { ... }),但接口的核心目的仍是定义行为契约。 本质:接口是“能做什么”的规范。 抽象类: 可以包含具体实现的方法(如 public void Eat() { ... })和抽象方法。 本质:抽象类是“部分通用功能 + 部分待实现功能”的模板。 -------------------------------------------------------------------- 3.接口可以多继承,抽象类只能单继承 很好理解了 接口: 一个类可以实现多个接口(多继承行为)。 接口之间也可以继承多个父接口(interface ICombined : I1, I2)。 抽象类: C# 不支持多重类继承,一个类只能继承一个抽象类(或普通类)
80. 构造函数是否能被重写??
解析查看
构造函数Constructor不能被继承,因此不能重写,但是可以重载
81. 静态成员和非静态成员的区别
解析查看
1.类成员用static声明,类被第一次加载到内存时创建,通过类访问 2.不带static的变量,非静态变量,在对象被实例化时创建,通过对象进行访问 3.静态方法里不能使用非静态成员(只能使用静态成员),非静态方法可以使用静态成员 4.静态成员是属于类的,而不属于对象
82. C#中ref和out关键字有什么区别??
解析查看
1.ref修饰引用参数。参数必须赋值,带回返回值,又进又出。 2.out修饰输出参数。参数可以不赋值,带回值之前必须明确赋值。 3.引用参数和输出参数不会创建新的存储位置。 4.如果ref参数值引用类型,方法里重新赋值后,原对象堆中数据回改变。 如果对引用类型再次创建新对象并赋值给ref参数,引用地址回重新指向新对象堆数据
83. 字符串中string str = null 和 string str = “” 和string str = string.Empty的区别??
解析查看
1.Empty是一个静态只读的字段 2. str ="" 初始化对象,并分配一个空字符串的内存空间 3. str = null ,只是声明了个变量,没有分配内存空间
84. const和readonly 有什么区别??
解析查看
1.初始化位置不同。 const必须在声明的同时赋值;readonly即可以在声明处赋值,也可以构造函数里赋值 2.const是编译时常量,在编译时确定该值 readonly是运行时常量,在运行时确定该值 3.const默认是静态的; 而readonly如果设置成静态需要显示声明 4.修饰引用类型时不同 const只能修饰string或值为null的其他引用类型; readonly可以修饰任何类型; 2.装箱——把堆中内容迁移到栈中去(引用转值) 3.如果问到值类型和引用类型同样,要引申这个
85. String s = new String(“XYZ”);创建了几个String Object
解析查看
两个对象 一个是”XYZ“,一个是指向这个字符串的引用对象s
86. C#是否可以继承String类??
解析查看
不能,类似于sealed修饰过,String在.Net里有特殊处理过
87. 结构体和类有何区别??
解析查看
结构体和类最大的区别是在存储空间上的 结构体是值类型,类是引用类型 结构体存储在栈上,类存储在堆上 结构体有面向对象中封装的特性,但是没有继承和多态。
88. C#两个对像值相同(x.equals(y) == true),但却可有不同的hashcode,这句话对不对???
解析查看
不对,有相同的hashcode (1)如果两个对象相同(equals方法返回true),那么它们的hashCode一定要相同。 (2)如果两个对象的hashCode相同,它们并不一定相同。
89. 用最有效的方法算出2乘以8等于几???
解析查看
用位运算,把2转换为2进制,左移3位即可
90. int?和int有什么区别??
解析查看
int? 为可空类型,默认值可以是null int 默认值是0 int?是通过int装箱为引用实现的
91. C# 语言中的sleep()和wait()有什么区别
解析查看
sleep()方法是将当前线程挂起指定的时间 wait() 释放对象上的锁并阻塞当前线程,直到它重新获取该锁
92. C#可否对内存直接操作
解析查看
C#在unsafe模式下可以使用指针对内存进行操作,但在托管模式下不可以使用指针,C#NET默认不运行带指针的
93.
Unity相关
1. Unity中点乘和叉乘对于我们来说的作用是什么?
解析查看
点乘: 1.可以判断对象的方位(前后),用我们的正朝向和指向对方的向量点乘。大于0就是后面,小于0就是前面。 2.还可以得到两向量之间的夹角 叉乘: 1.获得一个平面的法向量 2.得到两个向量之间的左右位置关系 注:Unity中用的是左手坐标系,所以确定方向的时候我们要用左手法则,最后得到的方向正好与右手法则相反
2. Unity中多线程执行下面哪些代码会报错?
A. Application.persistentDataPath
B. File.Exists(“文件名”)
C. transform.Translate
D. Object.Destroy(对象)
解析查看
A、C、D UnityEngine命名空间中相关类基本都不能被Unity多线程使用
3. Application.streamingAssetsPath 和 Application.persistentDataPath两个路径有何区别?对于我们的意义是什么?
解析查看
Application.streamingAssetsPath只读 Application.persistentDataPath 可读可写 ------------------------------------------------------------------- Application.streamingAssetsPath适合防止一些默认的2进制配置文件,用来读取初始化的数据 Application.persistentDataPath 用于处理数据持久化 ,或者作为热更新下载内容的存放目录
4. 请简述Unity中协程的原理。
解析查看
1.协程不是线程,Unity中的协程主要由迭代器实现,迭代器的原理是状态机 2.Unity协程主要是分成两个部分:协程函数和协程调度器。 协程函数会根据迭代器函数进行分布执行,遇到yiled return 再将程序挂起,去执行其他内容 挂起的函数一般在Update和LateUpdate 之间执行 协程调度器则则对协程函数进行统一管理
5. Unity底层如何处理C#代码
解析查看
1. Mono C#代码在项目发布时,会被编译成IL中间代码,最后这些中间代码会通过Mono虚拟机被编译成各大操作系统的机器码 2.IL2CPP 把IL中间代码转译为C++代码,再通过各平台的C++编译器直接编译为可执行的原生汇编代码。
6. Unity中当一个细小高速物体撞击另一个较大物体时会出现什么情况?如何避免?
解析查看
会出现穿模 1.我们可以尽量用射线检测来替代细小物体的物理系统碰撞 因为传统的FPS游戏都是通过射线检测加模拟计算来判断伤害的 2.修改Rigidbody刚体中的Interpolate(插值)和CollisionDetection(碰撞检测)两个参数,来提高碰撞检测的准确性
7. 请简述一下Prefab(预制体)的本质是什么?
解析查看
Prefab的本质就是一个配置文件 其中记录了一个GameObject对象上挂载的脚本信息 并且记录了脚本信息中的可配置的属性信息
8. Unity是否支持写成多线程程序?如果支持的话需要注意什么?
解析查看
答案: 支持 注意: 1.只能从主线程访问Unity相关组件、对象以及 UnityEngine命名空间中的绝大部分内容 2.如果多线程中要和Unity主线程同时修改一些数据 可以通过lock关键词加锁
9. 请简述一下对象池实现原理,在游戏开发中我们什么时候会用到它?
解析查看
1.我们把不用的对象放入对象池中失活,下次需要用到对象的失活再从对象池中取。线程池也是同样的原理 2.在游戏中频繁创建对象、实例化对象的地方,都可以用到对象池。 比如前端开发中,游戏中的子弹、伤害字体、特效等等 比如后端开发中线程池等等 3.对象池的作用就是: 避免大量创建对象再销毁对象时造成内存的消耗而引发GC。
10 .什么是DrawCall?DrawCall为什么会影响游戏运行效率?
如何减少DrawCall?
解析查看
1.CPU向GPU发送的一次渲染命令 2.DrawCall次数高,Cpu就要话更多的时间取准备渲染数据,这其中会进行更多的计算,进而影响游戏运行的效率。当每帧需要提交数千个DrawCall时,CPU可能忙于处理命令提交,会出现Cpu过载,而GPU因等待数据空闲 3. 如何减少DrawCall? 2D和UI层面:打图集,注意面板中不同图集照片的层级不要穿插 3D层面:使用动态批处理和静态批处理,尽量不要使用实时光照
11. transform.forward和Vector3.forword的区别
解析查看
答案: Vector3.forword始终时(0,0,1) 可以认为是世界坐标系的Z轴朝向 transform.forword是当前物体的局部坐标系的Z轴在世界坐标系下的朝向 可以认为是物体自己的Z轴朝向
12. Unity中如何解决过多创建和删除对象带来的卡顿问题?
解析查看
答案: 可以通过协同程序,分时分步创建或删除 原理是避免一帧中处理太多对象
13.
游戏中的成就系统,我们一般会使用设计模式中的哪种模式来制作?为什么?
解析查看
答案: 观察者模式 Unity基础小框架中讲解的 事件中心 就是基于观察者模式的 是一种订阅通知机制
14. 请简述热更新的流程
解析查看
答案: 本地存在资源对比文件和已有资源 1.下载资源服务器中的对比文件 2.将下载下来的远端对比文件和本地的做对比 记录需要更新的资源和要移除的资源 3.根据第二步中记录的信息,进行资源下载和移除 4.更新本地对比文件,其内容和刚才下载的远端对比文件一致
15. 我们应该如何优化UI(基于UGUI)
解析查看
答案: 性能上 1.打图集,将同一画面的图片放入一个图集中,目的是减少DrawCall 2.面板中的图片和文字尽量不要交叉,因为这样会产生多余的DrawCall 3.取消勾选不必要的射线检测,UI组件上的 4.减少透明图片的重叠使用 5.UI动静分离 因为UI本质就是mash网格,UI内部为了优化会把符合规则的ui网格进行合并。 而每一次UI动(比如:放大缩小)就要重新合并 那么如何分离? 将会动的UI放在另一个画布上,不动的UI留在原来的节点。 这样UI在动的时候,不会影响到不动UI的网格合并 等等 内存上 1.大图尽量使用9宫格缩放,让美术设计UI面板底图时不要过于复杂 尽量是有规律的纹理和颜色变化 2.图片的RGBA通道分离 等等
16. Unity中的Destroy和DestroyImmediate的区别是什么?
解析查看
答案: Destroy方法 可以指定删除的延迟时间,如果第二个参数不填写,最快也会在下一帧前完成删除。也就是如果Destroy对象后马上判空,该对象不会为空。 实际的对象销毁操作始终延迟到当前更新循环结束,但始终在渲染前完成 DestroyImmediate方法 会立即销毁删除对象(不会自动滞空,需要自己滞空)
17. 第一次执行GameObject.Instantiate时可能出现明显的卡顿如何解决该问题?
解析查看
答案: 我们可以通过Unity自带的性能分析工具Profiler分析实例化时造成卡顿的原因 程序上,一般我们可以从以下3个方面去优化它 1.相关资源加载:如果是由于资源加载带来的卡顿,我们可以在进入场景时进行资源预加载,总体思路就是将较大资源提前或者分帧加载 ------------------------------------ 2.脚本初始化:实例化对象时,会同步执行它身上挂载所有脚本的初始化工作,我们可以策略性的改变一些初始化逻辑,尽量不要再Awake和Start中做较复杂的逻辑,或者将复杂逻辑提前或者分帧处理 ------------------------------------ 3.对于会频繁使用的对象,我们可以使用缓存池 ------------------------------------ 美术上 不能只追求好的美术效果,而不考虑资源的消耗,要根据项目的实际情况,来设定模型的骨骼数、面数以及贴图的数量和大小上限。 在制作粒子特效时,粒子数、粒子面积、贴图等都要尽量少和小。 美术上要遵循:用最少的资源做出做好的效果,不能一味的用性能去换效果,最终会得不偿失
18. Unity使用IL2CPP打包时,我们应该注意什么?如何避免(可以举例说明)
解析查看
答案: 使用IL2CPP打包时,最可能出现的问题就是代码裁剪,IL2CPP会自动将它认为不会使用的代码裁剪掉,比如我们在使用Lua开发时,其实会用到很多UnityEngine或者我们自己写的C#代码,但是这些代码并不会在引擎中直接使用,都是在Lua中使用的,此时最容易出现的问题就是代码裁剪,导致打包后出现异常和报错。 --------------------------------------------------------------------------- 要避免IL2CPP的裁剪有3种方式,我们可以组合使用 1.设置打包时的裁剪等级 2.通过xml文件配置明确规定哪些内容不裁剪 3.在静态方法中显示调用不想被裁剪的内容
19. 两个四元数相乘有什么作用?四元数乘以向量有什么作用?
解析查看
四元数相乘:角度叠加 四元数乘以向量:向量旋转
20. 图中的小球是否被渲染了?是否会产生DrawCall?
解析查看
答案: 不会被渲染,不会产生DrawCall Unity本身有摄像机视锥体剔除,也就是不会显示完全位于视锥体之外的游戏对象 那么小球就不会进行渲染,也不会提交数据给GPU,也就不会产生DrawCall
21. 在没有使用遮挡剔除的情况下,图中A和B都是默认标准材质。图中的小球最终是否会被渲染,是否会产生DrawCall?
解析查看
答案: 最终不会被渲染,标准材质存在深度测试,小球在立方体后方,不会通过深度测试,所以不会被渲染 会产生DrawCall,既然都深度测试了,那么肯定是存在DrawCall的 深度测试发生在片元着色器处理之后,GPU会对每个片元执行深度测试来决定遮挡关系,决定是否被渲染
22. 如果不考虑IOS平台,只在Windows和Android平台上发布游戏,如何在不使用第三方热更新方案的前提下实现热更新功能
解析查看
答案: C#的反射 可以通过热更DLL文件的形式,加载程序集(dll) 利用反射执行热更DLL包中的逻辑
23. Unity中如何调试排查Android上运行的项目问题
解析查看
答案: 1.如果需要进行断点调试 通过数据线链接运行项目的Android设备 发布时开启了 Development Build 开发模式构建 Autoconnect Profiler 自动连接分析器 Script Debuggins 脚本调试 Wait For Managed Debugger 等待托管调试器 等选项 然后只需要Build and Run既可以 利用Unity的Profiler 性能剖析器窗口排查性能问题 并且还可以进行断点调试 ----------------------------------------------------- 2.如果只是获取一些打印调试信息 可以利用Unity2019.4及其以上版本提供的Android Logcat工具获取信息 Unity2019.4以下的版本,可以使用Android的ADB(安卓调试桥)工具 --------------------------------------------------------- 3.如果需要获取设备输入信息 可以利用Unity Remote来测试移动设备的输入相关逻辑
24. Unity引擎中哪些功能使用了C#的反射功能?至少说出一点
解析查看
答案: 1.Inspector窗口中显示的内容 2.预设体文件 3.场景文件 4.Unity中的各种特性 等等
25 . Unity中Awake和Start两个生命周期函数,分别在什么时候被调用?
解析查看
答案: Awake:运行时 当脚本被动态添加到对象上时立即被调用。 当对象被实例化时,依附它的脚本会立即调用Awake 它类似构造函数 Start:第一次Update之前被调用
26. Unity场景上有多个对象,都分别挂载了n个脚本。我们如何控制不同脚本间生命周期函数Awake的执行先后顺序?
解析查看
答案: 1.可以通过选中脚本文件,点击Inspector窗口右上角的Execution Order(执行顺序)按钮 2.可以打开Project Setting窗口,选择Script Execution Order选项 通过这两种方式我们可以打开脚本执行顺序窗口 在其中我们可以自己设置自定义脚本的执行顺序
27. 想要在Unity中使用指针我们需要进行哪些操作?
解析查看
答案: 1.需要在PlayerSetting中的OtherSettings中勾选 Allow 'unsafe' code 选项 2.使用指针时必须在unsafe修饰的代码块中
28. Unity中的协同程序中yield return不同的内容,代表的含义不同请说明下面这些yield return的含义
1.yield return 数字;
2.yield return null;
3.yield return new WaitForSeconds(数字);
4.yield return new WaitForFixedUpdate();
5.yield return new WaitForEndOfFrame();
6.yield break;
解析查看
答案: 1.yield return 数字; 下一帧执行 2.yield return null; 下一帧执行 3.yield return new WaitForSeconds(数字); 等待指定秒后执行 4.yield return new WaitForFixedUpdate(); 等待下一个固定物理帧更新时执行 5.yield return new WaitForEndOfFrame(); 等待摄像机和GUI渲染完成后执行 6.yield break; 跳出协程
29
.使用Unity协同程序进行异步加载时,底层是否会使用多线程?
解析查看
答案: 可能会 协同程序的原理是分时分步完成指定逻辑 在其中的某一步骤中,是可以使用多线程来完成某些加载操作的,多线程加载完成后,再进入协同程序的下一步继续执行
30. Unity中动态加载资源的方式有哪些?
解析查看
答案: 1.Resource类中的相关方法加载Resources文件夹下的资源 2.AssetBundle类中或Addressables类中的相关方法加载AB包中的资源 3.WWW类中或UnityWebRequest类中的相关方法加载本地或远端资源 4.还可以答 C#原生的一些文件加载相关 File FileStream等等
31. Unity中的光照贴图的作用是什么?
解析查看
答案: 在移动平台上(或配置较低的设备上)使用实时光源是非常消耗性能的 我们可以使用光照贴图,预先将环境光烘焙到贴图上,可以减少性能消耗
32.Unity场景中有两个点连成了一条线,想要旋转这条线,应该怎么做?
解析查看
答案: 两点相减得到一条向量,向量乘以四元数得到旋转后的向量,再把起点加上旋转后的向量即可得到转移后的线
33. LOD(多细节层次)和 MipMap(纹理图)的作用是什么?
解析查看
答案: 优化游戏性能 从不同距离渲染对象时,使用的是质量不同的模型(LOD)和贴图(Mipmap)。(一般情况是越远面数越低,图片越小)
34. 游戏开发中,客户端和服务端交互数据,程序中常用方式是什么?
解析查看
答案: 消息数据:Socket或HTTP 文件数据:FTP或HTTP
35. Unity中如何将本地坐标转为世界坐标?
解析查看
答案: 1.用本地坐标加上父对象相对世界的坐标(如果有多层父子关系,不停地往上加即可) 2.利用Transform中的TransformPoint方法
36.Unity中如何计算出两个向量之间的夹角,请说出两种方式
解析查看
答案: 1. 利用Vector3中的API:Vector3.Angle 2. 先使用 Vector3.Dot 算出方向向量点乘结果,再通过Mathf.Acos反三角函数算出弧度,再将弧度转为角度
37. 请写出UGUI中两种处理异形按钮的具体方法
解析查看
答案: 方法一:异形按钮,自带的像素检测阈值 方法二:异形按钮,通过子对象拼凑
![]()
- 第一步:将需要进行异形按钮判断的图片可读写打开
- 第二步:通过代码控制Image组件上阀值改变
38. 请说出Unity中如何进行数据持久化,至少说出5种方式
解析查看
答案: PlayerPrefs 2进制文件存储 xml文件存储 json文件存储 数据库存储(本地、远端、通过服务器存储到数据库)
39. 在Unity中如何控制渲染优先级?(谁先渲染谁后渲染,分情况回答)
解析查看
答案: 1.不同摄像机渲染时,摄像机深度(Camera depth)控制优先级 2.相同摄像机时,排序层级(Sorting Layer)控制优先级 3.相同排序层级时,层中的顺序(Order in Layer)控制优先级 4.相同摄像机,无排序层级属性时,Shader中的RenderQueue(渲染队列)控制优先级 -------------------------------------------- 1、camera(depth越小,越先渲染) 2、sorting layer(值越小,越先渲染)(下面还有个sortingOrder 值越小,越先渲染) 3、渲染队列renderQueue(值越小,越先渲染) 4、深度值(距离相机越近该值越小,越远该值越大。)
40. Unity中判断两个2D矩形是否相交,有几种方式?(请至少说出两种方式)
解析查看
答案: 1.使用Unity物理系统进行碰撞检测 2.使用Unity中范围检测相关API 3.自己写算法进行检测(STA分离轴检测) (太难了....)
41. Unity中想要制作角色的连招功能,在制作状态机时我们一般如何处理?
解析查看
答案: 1.状态机条件可以添加一个Trigger类型和Int类型 Trigger条件主要用于触发动作,Int条件主要用于连招计数判断,根据连招技术设置不同的条件 2.逻辑上,当攻击按键输入时,我们需要触发动作,并且进行攻击计数 通过连招输入窗口时间判断是否应该连招,当输入时间过了,就清0,每次攻击重置计时器 连招结束后再最后一个攻击事件调用计数清零
42. Unity中如果想要在动作的某一时刻进行伤害检测,我们应该怎么做?(请说出两种做法)
解析查看
答案: 1.添加动画事件 2.在切换动画一开始,进行延迟触发,延迟时间为想要触发伤害的时间(延迟可以用延迟函数,也可以用协同程序)
43. Unity中想要制作自动寻路逻辑,我们应该怎么做?(请至少说出两种做法)
解析查看
答案: 1.Unity自带的网格寻路系统 2.自定义寻路算法(比如 A星寻路算法)
44. 游戏编辑器(比如 角色编辑器、关卡编辑器、地图编辑器等工具)的本质是什么?
解析查看
答案: 数据的图形化编辑工具
45.Unity中使用的是左手还是右手坐标系?我们需要注意什么?
解析查看
答案: 左手坐标系 在进行向量相关计算时,要注意左手和右手坐标系的区别
46. Unity中鼠标、键盘、触屏、手柄等输入事件会在Update 之前、还是之后、还是同时执行?
解析查看
答案: 之前
47. Unity中场景中一个处于激活状态的物体(场景上只有这一个物体),不能被摄像机渲染出来,可能有几种情况?(至少说出3种可能的情况)
解析查看
答案: 1.在摄像机可视范围外(视口范围外) 2.在摄像机可视范围外(远近裁剪面之外范围) 3.物体的层级不能被摄像机渲染 4.该物体使用了透明材质,处于透明状态 5.该物体使用了单面渲染材质,摄像机看到的是该物体的背面 6.如果存在多摄像机,摄像机深度可能会影响 等等
48. Unity制作物理游戏相关功能时,我们采用哪种方式处理位移?为什么?
解析查看
答案: 通过刚体相关API来处理位移,比如加力、改变刚体速度变量 原因:在碰撞检测时能更准确无误
49. Unity热更新解决方案中,Lua和ILRuntime方案的本质是什么?
解析查看
答案: Lua热更本质: Lua是解释型语言,不需要实现编译,在运行时动态解释执行。xLua和toLua等Lua热更新解决方案,是通过在Unity中内置Lua虚拟机(解释器)来执行Lua逻辑的 --------------------------------------------------------- ILruntime热更本质: ILRuntime通过读取DLL文件中的IL汇编码,通过在Unity中内置的IL解释执行虚拟机(解释器)来执行热更DLL中的代码 ---------------------------------------------------------- 总体来说,他们都是在Unity中内置各自的解释器(写好的用于解释执行相关的代码)来执行我们的热更新代码
50. 请解释Unity中的Prefab是什么,以及它在游戏开发中的作用是什么?
解析查看
在Unity中,Prefab是一种游戏对象的预制件或者模板。它允许你在编辑器中创建一个游戏对象,并将其保存为Prefab,以便在需要时可以实例化多个相同的对象。当你在场景中使用一个Prefab实例时,它将保留与原始Prefab相同的组件、属性和位置,使得你可以轻松地创建和管理大量相似的游戏对象。 -------------------------------------------------------------------------------------------------------- Prefab在游戏开发中的作用非常重要,因为它们允许你创建可重用的游戏对象,并且可以在开发过程中快速迭代和修改。例如,当你需要在游戏中生成大量相似的敌人时,使用一个敌人Prefab可以使得你仅需创建和修改一个敌人对象,而不必重复编写和调整代码。此外,Prefab还允许你在编辑器中预览和调整游戏对象的属性和组件,使得你可以快速地进行修改和测试,从而加快开发进程。
51. CPU和GPU他们分别是什么?他们主要完成什么工作
解析查看
答案: CPU为中央处理器 负责算数运算、逻辑操作、数据传输等通用计算任务,同时还管理和调度计算机的资源 (游戏开发中—游戏逻辑处理),主要处理操作系统管理、程序执行、通用计算等等 CPU一般处理串行任务,即顺序执行的单个任务,适用于大多数常规计算 --------------------------------------------------------------- GPU为图形处理器 是专门用于图形和并行计算的处理器 显卡就是搭载GPU的硬件设备,显卡包含一个或多个GPU芯片,还包含显存(用于存储图像数据)、显示接口、视频解码器等等(游戏开发中—渲染相关处理) GPU主要处理并行任务,适用于图形渲染、3D游戏、机器学习、科学计算等需要大量并行计算的领域
52. Unity中的Lerp和Slerp分别是什么?
解析查看
答案: Lerp和Slerp是两个方法,在Mathf和Vector等类中都提供了对应的方法 1.Lerp: 线性插值函数,接受三个参数,起始值、目标值和插值比例 Lerp用于在两个值之间进行平滑的线性插值,适用于直线运动、颜色过渡等场景 ---------------------------------------------------------- 2.Slerp: 球面插值函数,接受三个参数,起始值、目标值和插值比例 Slerp在插值过程中会在两个旋转之间沿着曲线(球面)插值,从而保持较为自然的旋转过渡 ---------------------------------------------------------- 他们都是在Unity常用的插值函数,能够在动画、平滑移动、旋转等场景中产生自然和平滑的效果
53. Unity底层是单线程还是多线程
解析查看
答案: Unity底层实现是基于单线程的,这个线程通常被称为 主线程 或 渲染线程 意味着大部分的游戏逻辑、渲染和更新都在主线程上执行。 ----------------------------------------------- 但是Unity也支持自定义多线程处理复杂逻辑,并且Unity目前的版本也提供了一些多线程技术来改善性能,比如Job System、Dots等
54. 如何在Unity中实现多语言支持?(不同国家的人,看到的游戏内的语言是不一样的)
解析查看
答案: 1. 单包:将所有设计多语言的图片、文本通过配置表去配置 当显示这些内容时,根据配置表中的信息去动态的加载 所有地区通用一个安装包,只是游戏中进行地区判断或者语言选择 --------------------------------------------------- 2. 分包:根据不同地区发布不同的安装包
55. 在Unity中,有时在第一次执行GameObject.Instantiate的时候有明显卡顿,该怎么解决?
解析查看
1.装箱——把栈中内容迁移到堆中去(值转引用) 2.装箱——把堆中内容迁移到栈中去(引用转值) 3.如果问到值类型和引用类型同样,要引申这个
56. 在Unity中,有时在第一次执行GameObject.Instantiate的时候有明显卡顿,该怎么解决?
解析查看
答案: 通过Unity的Profiler窗口排查卡顿原因,一般有以下几种可能性 1.加载过大资源造成卡顿 解决方案: a.预加载资源 b.简化资源(修改图片大小,减少模型顶点、面数,压缩优化资源等) c.异步加载资源(分帧加载) ----------------------------------- 2.对象挂载的脚本中初始化耗时 解决方案: a.减少序列化或反序列化信息 b.优化初始化相关逻辑,提前初始化,分帧初始化(协程)等
57. 在Unity中AssetBundle的压缩方式有不压缩、LZMA、LZ4三种,请问LZMA和LZ4有什么区别?
解析查看
答案: LZMA:压缩算法为Lempel-Ziv-Markov chain-Algorithm 压缩包最小(压缩率更高),但是解压过程较慢,耗时较长(因为它需要进行更多的解压缩操作) ------------------------------------------------------- LZ4:压缩算法为Lempel-Ziv 4 压缩包较大(压缩率较低),但是不要求完整的数据包就可以解压缩,无需解压完整压缩包,解压时间快(几乎可以立即解压AssetBundle)
58. Unity中DrawCall、Batches、SetPass Calls的意思是什么
解析查看
答案: Draw Call: 表示渲染请求的数量(每个 Draw Call 都会引起一次从 CPU 到 GPU 的数据传输) 直接影响渲染性能,因为它决定了 CPU 和 GPU 之间的通信次数 减少 Draw Call 的数量通常是优化渲染性能的关键之一 可以通过使用批处理技术(Batching)来合并多个物体为一个 Draw Call 来实现 ----------------------------------------------------------------- Batches: 是一种将多个物体合并为一个 Draw Call 的渲染优化技术 它将多个相似的物体合并成一个 Draw Call,从而减少 CPU 到 GPU 的数据传输和渲染开销 可以使用静态批处理、动态批处理和GPU Instancing等技术来进一步优化 Batch -------------------------------------------------------------------- SetPass Call 渲染Pass(Shader中渲染通道)的数量 移动平台中尽量减少Shader中Pass的数量可以提升性能
59. Unity中摄像机组件中的投影(Projection)参数其中的两个选项
透视投影(Perspective)和 正交投影(Orthographic)
有什么区别?
解析查看
答案: 透视投影: 模拟人眼看世界的方式,近大远小,并呈现出显示世界中的景深和透视效果 通常用于制作3D游戏 -------------------------------------------------------- 正交投影: 不考虑对象到摄像机的距离,所有对象无论在场景中任何位置,都以相同大小显示 通常用于制作2D游戏
60. FSM和BT指什么?他们是用来处理什么的?
解析查看
答案: FSM(finite state machine):有限状态机 BT(Behavior Tree):行为树 ------------------------------------ 他们都是用来制作游戏中AI功能的设计方案
61.10000个monobehavior,每个各自执行update,和放到一个update里执行,哪个效率更高?为什么?
解析查看
答案: 放在一个Update中执行效率较高 原因: 1.因为每调用一次Update,相当于就是执行了一次方法 而每一次方法的调用在底层是有开销的,当在一帧里进行过多的方法调用自然会增加额外的开销 2.Unity内部对生命周期函数采用了引用和查找机制,每次调用Update时,也会存在查找相关操作,也会增加部分开销
62. 我们在编写代码时,有什么常用的优化代码性能的手段?至少说出3点
解析查看
答案: 1.间隔几帧执行一次逻辑(自己手动间隔、协同程序分时分步) 2.在帧更新函数中尽量少用临时变量(比如Update、OnGUI等每帧执行的生命周期函数中) 3.定时进行手动垃圾回收(GC)(比如切换场景时) 4.优化数学计算,比如尽量少用浮点数,用乘法代替除法等等 5.应用缓存池 6.尽量减少Update生命周期函数的数量 等等
63. OpenGL和DX的最大区别是什么?
解析查看
OpenGL和DX的主要区别就是在于 OpenGL是一个跨平台、跨语言的开放图形库,可以支持各种平台,比如Windows、Linux、MacOS等等 DX是微软创建的,它不跨平台,只针对微软相关平台,比如Windows操作系统,xBox主机系 ------------------------------------------------------------------ 游戏引擎底层就会对图形API进行封装,为了适应更多的平台,往往会针对多种图形API进行封装
64. Unity工程文件中,meta后缀的文件中主要存了什么信息?(最少说出2点)
解析查看
答案: 1.文件的全局唯一标识,用于确保资源文件在Unity中的唯一性 2.导入设置相关信息,比如纹理在Inspector窗口中的相关设置信息 3.关联脚本,如果资源和脚本关联,会包含相关脚本信息 4.资源依赖关系 5.编辑器状态,比如在Unity编辑中是否被锁定等 ----------------------------------------------------- meta后缀文件在进行项目版本管理时比较重要,因为它可以确保资源的一致性
65. 不同后缀的各种文件的本质是什么?
解析查看
答案: 不同后缀的文件表示文件的类型或格式不同 一般情况下,不同后缀的文件都有着自己的一套序列化和反序列化的规则
66. UnityWebRequest可以用来做什么?
解析查看
答案: 1.用于上传下载数据(网络通讯,网络传输) 2.用于本地数据加载 3.支持异步执行 它支持多种协议,比如HTTP、HTTPS、FTP、本地文件系统
67. 如何在Unity中进行多线程编程以提高性能?(至少说出3点可以使用多线程提高性能的内容)
解析查看
答案: 我们可以将一些复杂耗时的计算任务在Unity中使用多线程处理以提高性能 比如: 1.网络通讯 2.A星寻路 3.文件读写操作 4.使用协同程序异步加载(协同程序底层,部分API也是开启了多线程的) 等等
68. 游戏开发完成后1.游戏运行卡顿、设备发热一般往哪个大方向进行排查?
2.游戏运行一段时间后闪退,一般往哪个大方向进行排查?
解析查看
答案: 卡顿、发热:性能 闪退:内存
69. 游戏项目中,运行时主要占内存的内容有哪些?(至少说出5点)
解析查看
答案: 代码相关: 1.代码中预加载的数据(比如数据表中加载出来的数据) 2.代码中的成员数据(执行逻辑的代码中的相关成员) ----------------------------------------- 资源相关: 模型 3.模型数据 4.纹理贴图 5.动画数据 2D 6.UI 7.各种游戏图片(角色、道具、背景等等) 其他 8.音效 9.特效 -------------------------------------- 等等
70. 游戏项目中,主要消耗性能的内容有哪些?(至少说出3点)
解析查看
答案: 1.图形渲染相关 2.物理引擎 3.骨骼动画 4.资源加载 5.寻路算法 6.网络通讯 7.常规游戏代码逻辑
71. 网络游戏中,有一个数据统计界面,这些数据是由最近1000场战斗计算出来的平均数据。你认为以下处理方式合理吗?如果不合理,应该如何改进?
- 我们通过向后端请求这1000场战斗的数据,然后在客户端计算相关平均数据,更新显示到统计界面上。
解析查看
答案: 不合理 应该让后端计算好,前端需要数据时请求获取计算好的数据后更新到界面上即可 无需前端来进行计算处理
72. 如果在Unity当中制作FPS游戏,如何模拟枪械开枪时的后坐力
解析查看
答案: 每次开枪时 可以给枪械、手臂IK、摄像机等(根据实际视角情况决定) 一个绕x轴(往上偏移)和y轴(左右偏移)的旋转角度 并且每帧都会归位(角度四元数不停向原始位置归位)
73. Unity当中存在多线程时,继承MonoBehaviour的脚本是否有必要对其中内容加锁?为什么?
解析查看
答案: 分情况讨论 1.如果是想要通过多线程控制或访问场景上对象的相关行为,比如移动旋转、资源加载、动态销毁等等,那么不用加锁 因为Unity的整个执行模型是单线程的,所有的游戏逻辑和渲染都在主线中进行。 大部分的UnityAPI都只能在主线程上调用,如果通过多线程去调用Unity场景上物体的相关API,或改变场景中对象的相关属性,会直接出现报错。因此加锁也没有意义 ---------------------------------------------------------------------- 2.如果是想要通过多线程改变继承MonoBehaviour脚本中的某些公共成员,比如一个int,一个List。那么可以为这些公共成员加锁,防止线程并发带来的问题 -------------------------------------------------------------------- 但是建议尽量不要通过多线程和继承MonoBehaviour的挂载在场景对象中的脚本打交道
74. 我们在Unity中进行一些复杂逻辑处理时,比如网路通讯、寻路算法。往往会开启多线程进行处理。我们如何保证数据能够和Unity主线程进行正常交互?(请至少说出1种方式)
解析查看
答案: 1.对共享数据加锁,避免多线程并发带来的数据竞争问题 ---------------------------------------------------------- 2.是用C#中线程安全的数据结构存储共享数据 比如System.Collections.Concurrent 命名空间中的ConcurrentQueue<>和ConcurrentDictionary <> -------------------------------------------------------------- 3.可以尝试使用Unity当中的Dots系统 ------------------------------------------------------------------ 等等
75.Unity生命周期函数中的OnEnable和Start,我们在使用时应该如何选择?可以举例说明。
解析查看
答案: 在对象的整个生命周期中 OnEnable在对象激活时都会自动调用一次 Start在第一次Update调用之前只会调用一次 ---------------------------------------------- 我们需要根据执行时机的这个特点来决定何时调用他们 OnEnable可以将一些想要重置的数据 放在其中处理 Start一般放只会初始化一次的数据内容
76.
Shader相关
1. 解释一下Unity中的渲染管线(Rendering Pipeline)是什么,如何使用它,并举例说明它在游戏开发中的应用场景。
解析查看
渲染管线分为应用阶段、几何阶段和光栅化阶段 1.应用阶段 主要是CPU准备一些数据,比如顶点、法线、贴图和光照数据等等 2.几何阶段 几何阶段主要为GPU计算数据,如顶点计算、坐标转换、裁剪等等。 顶点着色器-》曲面细分着色器——》几何着色器——》裁剪——》屏幕映射 3.光栅化阶段 根据几何阶段的信息来计算每个片元覆盖哪些像素,经过深度测试、透明混合确定最终渲染的颜色。 三角形设置——》三角形遍历——》片元着色器——》逐片元操作
2. 渲染管线中的几何阶段是由CPU主导还是GPU主导?该阶段主要是在做什么?
解析查看
答案: 渲染管线中的几何阶段主要由GPU主导 在几何阶段中完成的最主要工作就是 顶点、法线等模型数据的相关处理 对他们进行坐标空间转换,裁剪画面外图元等等 还可以进行顶点着色,为每个顶点计算颜色、纹理坐标、法线等属性,以便在后续阶段用于处理
3.齐次坐标是什么?对于我们有什么作用?
解析查看
答案: 是什么? 就是将一个原本是 n 维的向量或矩阵用 n + 1维来表示 是一种扩展的坐标系统,,用于在计算机图形学等学科中进行数学运算 对于我们的作用? 利用齐次坐标,我们可以明确的区分向量和点,能够表示出平移变换
4. Shader开发中的坐标空间变换,主要是在哪几个空间中进行变换?变换顺序是什么?
解析查看
答案: 主要是将模型相关数据(顶点、法线、切线等)在 模型空间 —> 世界空间 —> 观察空间 —> 裁剪空间 —> 屏幕空间 之间进行变换
5. Shader当中的顶点和片元着色器有什么作用?
解析查看
答案: 顶点着色器: 主要作用是对物体的顶点位置进行变换和投影,从本地坐标系转换到世界坐标系和相机坐标系等。在这个阶段,顶点着色器还可以计算光照的影响、法线的变换,以及传递一些数据供后续阶段使用 -------------------------------------------------------------------------------- 片元着色器: 主要处理像素的颜色、纹理和光照。在顶点着色器之后,渲染管线会对物体进行光栅化,将物体的几何形状转换为像素。在片元着色器中,针对每个像素,会进行插值和纹理采样,以及计算光照、阴影、反射等效果。片元着色器最终输出的颜色将决定每个像素的显示效果。 ---------------------------------------------------------------------------------- 这两个着色器阶段协同工作,将3D场景中的几何信息转换为2D屏幕上的像素颜色,从而实现图形的渲染和显示。
6. Unity Shader中,深度测试是在做什么?
解析查看
答案: 深度测试通过比较像素的深度值,来确定哪些像素应该被绘制到屏幕上,并决定它们的可见性。 深度测试的主要目标是解决遮挡关系,确保前面的对象覆盖后面的对象,从而正确呈现场景
7. Unity Shader中某片元通过了深度测试,但是没有开启深度写入,该片元的颜色信息是否写入到颜色缓冲区?
解析查看
答案: 会写入颜色缓冲区 因为深度写入和颜色写入是两个独立的操作 只要通过了深度测试,不管是否写入深度缓冲区 该片元的颜色信息都会写入到颜色缓冲区中
8. Shader当中的Blend渲染命令主要用来干什么?
解析查看
答案: 用来进行颜色混合的 将当前片元的颜色和颜色缓冲区中的颜色进行混合计算 ------------------------------------------------------ 一般可以用来实现半透明相关效果,光照颜色叠加相关效果等等
9. Shader当中的Blend渲染命令后面跟的参数是用来干嘛的?
比如:
Blend SrcAlpha OneMinusSrcAlpha
解析查看
答案: Blend混合命令后面跟的参数有两种写法 这些因子其实就是一些特定的数,用于参与混合计算 写法一: 输出颜色rgb = 源因子 * 片元颜色的rgb + 目标因子 * 颜色缓冲区中颜色的rgb 输出颜色a = 源透明因子 * 片元颜色的a + 目标透明因子 *颜色缓冲区中颜色的a 写法二: 输出颜色rgb = 源因子 * 片元颜色的rgb + 目标因子 * 颜色缓冲区中颜色的rgb 输出颜色a = 源因子 * 片元颜色的a + 目标因子 *颜色缓冲区中颜色的a
Lua相关
1. Lua如何实现面向对象的三大特性?
解析查看
答案: 面向对象三大特性 封装:利用table进行封装 继承:利用元表和__index模拟继承关系 设置子类的元表为父类,父类的__index为父类自己 当子类身上找不到对应属性和方法时 会查找元表的__index中的内容,也就是会查找父类中的内容 通过这种方式来模拟继承 多态:子类自己去实现带:的同名方法即可
2. 请说出Lua中常用的数据类型(至少说出6种)
解析查看
答案: nil boolean number string table function userdata thread
3. Lua中pairs和ipairs的区别
解析查看
答案: pairs可以遍历出表中的所有键值对信息,在遍历不规则表时,建议用它 ipairs只能遍历出连续信息,对于不规则结构,可能会缺失
4. Lua中常用的元方法有哪些?至少说出3个元方法
解析查看
答案: __index __newindex __tostring __call __add 运算符+ __sub 运算符- __mul 运算符* __div 运算符/ __mod 运算符% __unm 运算符- __concat 运算符.. __eq 运算符== __lt 运算符< __le 运算符<= 2.装箱——把堆中内容迁移到栈中去(引用转值)
5. Lua中元表的作用
解析查看
答案: 为一个表设置元表后,该表在某些特殊行为下可以调用元方法 比如:把表当字符串打印时,会调用元表的_tostring 当访问该表中不存在的变量时,会通过_index指定的表去找
6. Lua中__ index和__ newindex有什么作用
解析查看
答案: __index用于查找:如果访问不存在的数据,由__index提供最终结果 __newindex用于更新:如果对不存在的数据赋值,由__newindex对数据进行赋值
网络相关
1. 网络游戏开发中,网络传输数据的基本流程是什么?
解析查看
答案: 客户端将自定义类对象数据序列化为2进制数据发送给服务端 服务端将收到的2进制数据反序列化为对应的类对象进行逻辑处理 如果是服务端发送给客户端的消息也是同理
2. 网络通讯中分包黏包指的是什么?我们应该如何解决这些问题?
解析查看
答案: 分包:一个独立的数据包(消息)被拆分成了多个小数据,分别发送出去 黏包:多个数据包(消息)被粘合在了一起,变成了一个更大的数据包,发送出去 解决:加入消息头,在消息头中加入长度等其他信息,用于接收后判断消息长度,进行对应的逻辑处理
3. TCP和UDP通讯最大的区别是什么?
解析查看
答案: 安全性上: TCP是安全可靠的,有序、无差错、不丢失 而UDP不保证可靠性,发送的数据可能会丢失,也可能会乱序 -------------------------------------------- 效率上: UDP通讯效率更高
4. 如果想要让UDP通讯具备TCP的优点,应该如何处理?
解析查看
答案: 主要思路就是基于UDP模拟出TCP的安全性 主要做法: 1.为消息加入序号:为UDP发送的每一个消息加入一个序号 2.加入消息确认机制:接收端在成功接收到一个消息后,向发送端发送一个确认消息,表示该消息已经被正确接收。 3.加入超时重传机制:发送端在超时时间内未收到确认,则进行消息的重传。
数学相关
1. 是不是所有矩阵都有逆矩阵?如何计算一个矩阵的逆矩阵?
解析查看
答案: 1.并不是所有矩阵都有逆矩阵 2.计算流程 第一步:确定矩阵是否为方阵(即行列数相等) 第二步:计算矩阵的行列式(若行列式为0,则该矩阵没有逆矩阵) 第三步:计算矩阵的代数余子式矩阵 第四步:计算标准伴随矩阵(转置代数余子式矩阵) 第五步:计算逆矩阵(标准伴随矩阵 / 行列式)
算法相关
1. 请问七大排序算法一般指哪七种排序算法?你一般常用的排序算法是哪种?请简单描述它的排序原理
解析查看
答案: 冒泡排序 选择排序 插入排序 希尔排序 归并排序 快速排序 堆排序
2. 请简单描述斐波那契数列的基本规则是什么
解析查看
答案: 假设数列从索引0开始 斐波那契数列的基本规则就是从数列的第2项开始,每一项的值都是前两项的和: F(n) = F(n-1)+F(n-2) n >= 2 即: 1,1,2,3,5,8,13,21.......
3. 请简单描述A星寻路算法的基本原理
解析查看
答案: 关键点: 1.寻路消耗公式:f(寻路消耗) = g(离起点距离) + h(离重点距离) 2.开启列表 3.关闭列表 基本原理: 每一次寻路,计算周围点的寻路消耗,并放入开启列表,对开启列表进行排序,得到寻路消耗最小的点放入关闭列表中
4. 递归函数是什么?请用一句话总结
解析查看
答案: 递归函数就是在函数定义中使用自己的函数(自己调用自己)
5. 在编写递归函数时,最需要关注的两点是什么?请说出你自己的理解
解析查看
答案: 1.结束条件:在什么情况下递归会结束,否则会死循环 2.问题规模的减小:递归函数通过自身来解决问题的规模,每次递归都应该将问题分成一个或多个更小规模的子问题,直到达到结束条件 --------------------------------------------------------------- 这两个关键点都是为了确保递归函数不会无限的执行下去,避免死循环
6. 二分查找(折半查找)的原理是什么?
解析查看
答案: 在一组有序(升\降)的数据中查找一个元素时 将目标元素和查找范围的中间值做比较 若目标==中间值,查找结束 若目标 != 中间值,将目标分到较大\较小的一组 通过分组,可以将查找范围缩小一半 再新租中重复刚才的步骤,直到找到目标
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 游戏开发笔记!
评论