Unity面试题

目录

C#知识点:

1.多态是什么??

2.值类型和引用类型区别

3.请简述GC(垃圾回收)产生的原因,并至少说出避免GC发生的三种方式?

4.List和Dictionary的区别,还有了解过哪些数据结构,它们的特性是什么?

5.string、stringBuild、stringBuffer区别

6.面试题虚函数相关:

7.string、int、float、interface、enum、Object、struct、UnityEngine.Color、UntiyEngine.Ray哪些是值类型哪些是引用类型

8.类和接口的区别:

9.Array、ArrayList、List、Hashtable、Dictionary、Stack、Queue区别:

10.浅拷贝和深拷贝

11.ref和out的区别

Unity知识点:

1.Unity中生命周期函数执行顺序

2.协程是什么

3.点乘、叉乘和归一化的几何意义是什么?

4.Collider和Trigger区别,碰撞的必要条件是什么?

5.了解过四元数吗??

6.Unity提供了几种光源,分别是什么?

7.委托和事件的区别:

8.Mono和IL2CPP的区别

排序算法:

1.用快排写一个逆序:

简单理解

Lua知识点:

1.Lua有几种变量类型

2.以下代码结果是什么:local table = {1,2,[2] = 3}

3.代码结果是什么​编辑:

4.遍历中ipairs和pairs区别是什么

5.元表中的__index、__newindex、__call、__tostring、rawset、rawget是什么

6.table如何判当前是空表,如何获取table长度

7.function中的点和冒号的区别

8.闭包是什么

9.C#如何调用Lua

10.Lua如何实现面向对象三大特性

UI框架

渲染优先级

运行时改变UI层级

Protobuf

Protobuf优缺点


C#知识点:

1.多态是什么??

执行同一操作(方法),实现不同效果

2.值类型和引用类型区别

1.存储方式不同:

  • 值类型存储在栈中,引用类型存储在堆中
  • 值类型在栈中直接存储对应的数据。引用类型在栈中存储的是地址,在堆中存储的是对应的数据
  • 值类型是自动释放,引用类型需要触发GC回收

2.执行速度不同:值类型执行速度快,引用类型执行速度慢

3.请简述GC(垃圾回收)产生的原因,并至少说出避免GC发生的三种方式?

堆中的容量达到一定界限,就会自动触发GC垃圾回收机制。

避免GC方式:

1.少用string,使用stringBuild

2.使用公共对象,少new

3.使用对象池

底层原理:三代标记清除压缩。在分配空间时导致空间碎片化、使用临时对象,用完后无引用时就会变成垃圾。

4.List和Dictionary的区别,还有了解过哪些数据结构,它们的特性是什么?

-----------------------------------底层原理面试官没问到时,可不回答-------------------------------------------

List是顺序表,相当于容器用于装载相同类型的数据。

List底层实现原理:底层通过数组实现,在添加数据时如果空间不够了,它会自动以1倍的方式扩容(比如目前只有2byte空间,需要的空间需要7空间,它会自动2*2*2=8)

List每次扩容时,都会创建一个新的空间,然后把原空间的数据放到新创建的空间;从而原空间就变成垃圾了,需要等待GC回收

Dictionary是散列表,以键值对的方式存储对应的数据

Dictionary底层实现原理:底层通过数组和链表的方式实现。

1.在申明空间长度时通过C#官方的质数数组进行判断(数组里面全是质数),如果需要的长度小于数组中的质数,长度就为数组中的质数。

2.通过两个数组,Buckets数组和Entries数组进行数据存储,Buckets数组初始化时会将此数组数据都置为-1,Entries数组通过Hash算法,存储对应的Dictionary的值

Queue:队列,先进先出。

Stack:栈,先进后出。

5.string、stringBuild、stringBuffer区别

string字符串内容被改变时,会创建一个新的空间,然后将当前字符串内容传给新开辟的空间,当前字符串空间也变成了垃圾等待GC回收。

  • 创建string会放入字符池中:通过以下三种方式才会放入字符池中,字面量值new string、字面量值拼接、string.Internal
  • 字符串底层是通过键值对的方式存储数,键是字符串内容、值是字符串在堆中的地址;创建一个字符串时会在字符池中搜索是否有当前字符串内容的键,如果有就用相同的键和值,如果不存在就创建对应的键和值

stringBuild和stringBuffer都只会开辟一个空间。

stringBuild是单线程,线程安全。性能比stringBuffer较好一些

stringBuffer是多线程,线程不安全。性能比stringBuild较差一些

三者性能比较:stringBuild > stringBuffer > string

6.面试题虚函数相关:

 

因为Model2继承Model类后,Fun函数被覆盖了,不会执行;只会执行父类的Fun函数 。而Model3override没有base,只会执行当前类的Fun

7.string、int、float、interface、enum、Object、struct、UnityEngine.Color、UntiyEngine.Ray哪些是值类型哪些是引用类型

值类型:int、float、enum、struct、UnityEngine.Color、UnityEngine.Ray

引用类型:string、interface、Object、

8.类和接口的区别:

1.类只能被继承一个,接口可以继承无限制个。

2.继续接口后,里面的方法必须要被实现;类只有抽象方法才必须要被实现

3.接口里面不能实现方法;类里面可以实现方法

4.接口里面不能赋值变量,类可以赋值变量

9.Array、ArrayList、List、Hashtable、Dictionary、Stack、Queue区别:

Array(数组):存储在栈中。需要确定长度,静态存储在连续的内存中,查找速度O(1),但是插入或删除需要移动大量数据O(n)

链表:存储在堆中。动态存储在不同的内存块中,查找速度要遍历每个节点所有为O(n),插入删除为O(1)

ArrayList:存储的类型是Object,通过动态扩容,可以存储不同类型的变量,存储不同类型的变量也会频繁的产生装箱拆箱

List:存储的类型是泛型,通过动态扩容,不可以存储不同类型的变量,不会产生装箱拆箱

Hashtable:键值对字典,键和值都是Object类型,写入是单线程,读取是多线程

Dictionary:键值对字典,键和值都是泛型,读\写都是单线程

Hashtable和Dictionary区别:单线程建议使用Dictionary,因为是泛型读取效率较快,容量利用更充分。

在多线程中Dictionary不安全,需要通过lock锁进行处理

Stack:栈,先进后出

Queue:队列,先进先出

10.浅拷贝和深拷贝

最直观的是值类型和引用类型,值类型是深拷贝,引用类型是浅拷贝(引用类型也可以深拷贝)

浅拷贝:B复制A,A的内容进行改变,B的内容也会进行改变

深拷贝:B复制A,A内容改变,B内容不会改变

11.ref和out的区别

ref:使用前需要初始化,想让值类型当引用类型使用

  • 举例:比如确定链表的节点总数是单数还是双数,可以事先定义一个bool变量isOdd。然后利用函数将isOdd为参数,GetListNodeSum(ListNode head,ref bool isOdd)。当函数逻辑将isOdd改变时,函数外的isOdd也会受影响

out:使用时不需要初始化,快捷使用(输出新的值)

  • 举例:比如在道具系统中,字典存储了道具id和道具数据。那么想获取指定道具可以使用out,dic.TryGetValue(道具id,out item)

参考: ref 和 out 关键字详解_c#中ref和out关键字-CSDN博客

Unity知识点:

1.Unity中生命周期函数执行顺序

Awake->OnEnable->Start->FixedUpdate->Update->LateUpdate->OnGUI->OnDisable->OnDestroy

初始化时间:Awake(对象实例化时)->OnEnable(对象激活时)->Start(对象实例化完后执行)->FixedUpdate(每帧固定物理帧)->Update(渲染前的每帧,可以执行人物移动等等)->LateUpdate(渲染后的每帧,可以执行相机跟随等等)->OnGUI(渲染和处理GUI事件)->OnDisable(对象失活)->OnDestroy(对象销毁)

【扩展问题:Awake为什么不能执行协程???】

因为对象在实例化时就会执行Awake,对象都没有实例化好,就去调用对象自然会报错或异常。

建议在Start启动协程

2.协程是什么

协程是通过IEnumerator迭代器实现的一种状态机。协程并不是单独的线程,它属于主线程的一部分,如果协程卡死那么主线程也会卡死

协程函数通过yield return语法糖分成各个代码块,其中迭代器中有MoveNext函数和Current属性,MoveNext函数用来确定迭代器是否执行完(MoveNext的返回值如果为true代表还有下一个代码块,如果为flase代表执行完了);Current存有yield return对象,然后转换为IEnumerator对象返回给协程函数

3.点乘、叉乘和归一化的几何意义是什么?

点乘可以计算两个向量之间的夹角。判断对象在自己的前方还是后方

叉乘用于判断对象在自己的左方还是右方

归一化:没有大小,只有方向的向量

4.Collider和Trigger区别,碰撞的必要条件是什么?

Collider是碰撞器,Trigger是触发器。触发器是基于碰撞器实现的,触发器在碰撞器的Inspector面板下

它们都是物理系统。碰撞器类似于现实世界的物体,无法穿透。而触发器可以穿透物体

必要条件:双方都有Collider碰撞器,至少一方有RigidBody刚体

5.了解过四元数吗??

用于处理旋转方面。相对于欧拉角,四元数不存在万向节死锁;在Inspector面板上所看到的也没有那么直观,只能看到180至-180度,不能很好的确定物体所旋转了多少圈

6.Unity提供了几种光源,分别是什么?

Directional Light:平行光

Point Light:点光源

Area Light:范围光。这个稍微有点特殊,需要把被光源照射的物体设置为static(我的是cube对象);还有在Windows选项卡--Rendering--Lighting Setting--界面最下面勾选Auto Generate(自动生成)。不然创建了范围光,但是在场景中看不到效果

【我的是2019版本,我看别人那些太老的版本没有Auto Generate的选项】

Spot Light:聚光灯

7.委托和事件的区别:

委托的本质是一个类,是用来装载函数的容器,可以+=、-=添加或者删除函数

而事件是委托类型的一个变量

委托可以在定义外出用=赋值函数,但是事件只能在定义的时候用=赋值,不可在外部用=赋值

8.Mono和IL2CPP的区别

Mono和IL2CPP都是Unity打包的一种方式

  • Mono:将C#代码转换为CLR(中间代码),然后通过各平台的Mono虚拟机转换为机器码
  • IL2CPP:IL2CPP会直接转换为C++代码,然后通过C++编辑器转换为机器码

IL2CPP比Mono性能更好;Mono可以通过ILSpy反编译,但是IL2CPP不能反编译

排序算法:

1.用快排写一个逆序:

简单理解

理论

  • 选取基准,产生左右标识,左右比基准,满足则换位
  • 排完一次,基准定位
  • 左右递归,直到有序

我自己的简单理解

  • 确定左右标识、左右基准、基准值(一开始为左基准值,也就是下图的数值3)
  • 当前值为基准值,然后与左右基准值进行比较,通过换位确定基准值应该存在的位置

交换位置:基准值为3,从右基准的位置开始找比3小的数放到左边;然后再从左基准开始找比3大的数放到右边。

  • 缩小范围继续比较(除基准值以外,确定两个区间范围)

static void Main(string[] args)
        {
            //测试数据
            int[] array = { 1, 4, 2, 5 };
            //将数据排序
            QuickSort(array,0,array.Length  - 1);
            //排序后的数据
            foreach (int arr in array)
            {
                Console.WriteLine(arr);
            }
        }


        public static void QuickSort(int[] arr, int left, int right)
        {
            if (left >= right) return;
            int tempLeft, tempRight, temp;
            tempLeft = left;
            tempRight = right;
            temp = arr[left];

            while (tempLeft != tempRight)
            {
                while (tempLeft < tempRight && arr[tempRight] < temp)
                {
                    tempRight--;//移动右标识
                }
                arr[tempLeft] = arr[tempRight];//处理小于基准值的数,移动到左边

                while (tempLeft < tempRight && arr[tempLeft] > temp)
                {
                    tempLeft++;//移动左标识
                }
                arr[tempRight] = arr[tempLeft];//处理大于基准值的数,移动到右边
            }
            arr[tempLeft] = temp;//基准值通过排序,确定它应该放的位置

            //缩小范围处理,除基准值之外的范围
            QuickSort(arr, left, tempLeft - 1);
            QuickSort(arr, tempRight + 1, right);
        }

Lua知识点:

1.Lua有几种变量类型

8种。number、string、bool、table、function、Thread、userdata、nil

2.以下代码结果是什么:local table = {1,2,[2] = 3}

会首先将[2]=3存储到内存,然后再将数字索引1和2存储到内存,数字2存储的时候会覆盖[2]=3。最后的输出结果为:1,2

3.代码结果是什么

结果为1,因为ipairs遍历碰到nil就会中断

4.遍历中ipairs和pairs区别是什么

ipairs:只能遍历数字,有序遍历,在遍历的过程中如果碰到nil就会中断,不会再往下遍历

pairs:所有的类型都可以遍历,无序遍历,可以遍历table中的所有变量

5.元表中的__index、__newindex、__call、__tostring、rawset、rawget是什么

__index:如果通过setmetatable设置了元表,获取当前表没有的变量,就会去元表的__index寻找该变量。如果__index是函数,那么就执行函数(如果是表,则在表中寻找变量)

__newindex:赋值当前表中没有的变量,则会调用__newindex。__newindex赋值的是表就修改表,如果是函数就执行函数

__call:执行表中传入的参数为数值时,则会调用__call

__tostring:将当前表赋值给其他对象时,则会调用__tostring

--rawset会绕开__newindex原方法
local meta12 = {}
meta12.__newindex = {}
local t12 = {}
setmetatable(t12,meta12)
t12.age = 17
rawset(t12,"name","小白白")
print(t12.age)--nil
print(t12.name)--17
--绕开元表中的__index方法
local t12 = {}
local meta12 = {}
meta12.__index = {a = 11}
setmetatable(t12,meta12)
print(rawget(t12,a))--nil
print(t12.a)--11

6.table如何判当前是空表,如何获取table长度

如果确定table中只有数字一种类型,那么可以使用#获取table长度。否则使用table.getn(),此函数不能在在线编辑器中使用,因为在线编辑器没有对应的库

7.function中的点和冒号的区别

点:直接调用函数

冒号:将方法的对象隐含的传给function的第一个参数。函数使用冒号创建函数时,那么在使用函数的时候也要使用冒号;如果使用点的方式调用函数,需要显示的传入第一个对象(表)

8.闭包是什么

闭包我们可以简单理解为有“外部函数”和“内部函数”,当“外部函数”执行完了,“内部函数”依旧可以调用“外部函数”的变量。其中“外部函数”的变量是地址,以C#为例如果是值类型,那么执行完函数该变量就会自动销毁,这很明显不满足闭包的特性

9.C#如何调用Lua

lua是通过c语言制作的。其中c语言可以直接调用lua,所以C#会通过一些接口调用c语言,然后c语言在调用lua

其中调用流程:C#调用c,c调用Lua

10.Lua如何实现面向对象三大特性

面向对象三大特性
封装:利用table进行封装
继承:利用元表和__index模拟继承关系
          设置子类的元表为父类,父类的__index为父类自己
          当子类身上找不到对应属性和方法时
          会查找元表的__index中的内容,也就是会查找父类中的内容
          通过这种方式来模拟继承
多态:子类自己去实现带:的同名方法即可

UI框架

渲染优先级

Sorting Layer(只能Sprite,可在组件中添加Sprite Renderer) > Sort Order > PlanceDistance > Hierarchy中顺序

  • Sorting Layer(越小越先渲染)
  • Sort Order(越小越先渲染)
  • PlanceDistance(越大越先渲染,越大离摄像机越近)

运行时改变UI层级

  • transform.SetAsFirstSibling -- 最先渲染
  • transform.SetSiblingIndex(N) -- N为0和transform.SetAsFirstSibling渲染顺序一样
  • transform.SetAsLastSibling -- 最后渲染

Protobuf

Protobuf优缺点

优点:通过二进制的方式编码。体积小,传输和解析速度快。跨平台

缺点:可读性差。需要编译时静态编译,不能动态添加、删除字段

参考:protobuf的优缺点_protostuff优缺点-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值