Unity面试题(尽量补全)

1:重载和重写的区别?

重载(Overload)发生在一个类内部,方法名称相同,参数个数,次序,类型不同,对返回值没有要求。

class Cat
{
    public string name = " ";
    public Cat(string name )
    {
        this.name = name;
    }
    public Cat()
    {
        this.name = "无名";
    }

}

重写(Override)是发生在继承中,被重写的方法一定标记成Override和Abstract

2:面向对象三大特点?

封装,继承,多态

3:值类型和引用类型区别?

C#的所有值类型均隐式派生自System.ValueType

值类型包括 byte,short,int,long,float,double,decimal,char,bool 和 struct,enum

引用类型是string,class,接口,委托,数组

引用类型在声明后,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间;当使用 new 创建一个类的实例时,分配堆上的空间。

4:什么是装箱拆箱,怎么减少该操作?

装箱是把值类型转换为引用类型

比如在ArrayList中添加值类型的元素0,先把值类型的元素转换为object,然后再转换为引用类型

这种转换是隐式的,也有强转换

拆箱是把引用类型转换为值类型

牵扯到装箱拆箱比较多的操作是在集合中,比如ArrayList或者 HashTable等

5:private, public protected,internal?

private,类内私有

public,所有类公开

protected,对该类和派生类公开

internal,只能在包含该类的程序集中访问该类

6:ArrayList和List区别?

ArrayList不带泛型,数据容易丢失,它内含各种元素时需要装箱拆箱

List带泛型,数据不容易丢失,不牵扯装箱拆箱

7:堆栈?

堆和栈是内存中的两块区域

栈中存放的是对象的引用及对象方法中参数的值,由操作系统自动分配释放(先进后出)

栈的生长方向向下,内存地址由高到低

堆中存放的是实例对象及成员变量的值(属性的值),堆由开发人员分配和释放, 若开发人员不释放,程序结束时由 OS 回收(先进先出)

堆的生长方向向上,内存地址由低到高

8:GC产生的原因,如何避免?

Grabage Collection垃圾回收机制,遍历应用程序在Heap上动态分配的所有对象,通过识别它们是否被引用来确定哪些对象是已经死亡的,哪些仍然需要使用,回收已经不再使用的对象

如何避免:

1):减少使用new创建对象的次数,在创建对象时会产生碎片

2):使用共有对象,比如static等

3):在拼接大量字符串时,使用stringBuilder

4):使用对象池

5):避免使用foreach,尽量使用for循环

9:string 和 stringBuilder的区别?

string是引用类型,在堆上分配内存,运算时会产生一个实例,一旦生成是不可变的

stringBuilder是可变类,每次内容发生改变,不会新生成对象,在字符串拼接方面可以使用

10:简述对象池,在FPS中那些东西适合使用对象池?

对象池存放需要被反复调用的对象,比如游戏中的子弹,敌人等

11:for循环和foreach循环的区别?

for循环是一般性的循环,而foreach是专门用于迭代的集合循环方法,能够有效减少访问次数,达到优化的目的。

但是foreach循环不能对内部元素进行修改,只能访问,是只读的

12:ref和out关键词有什么区别、

值传递和引用传递的区别

Ref有进有出

Out只出不进

举例说明:

当不使用ref关键字的时候,打印出结果是1,我们这时候传递进函数内部的是我们number的拷贝,也就是复制了一遍number的值,我们称这种传递参数的方式为值传递

public class StringBuilder : MonoBehaviour
{
    private void Start()
    {
        int number = 1;
        Method(number);
        Debug.Log(number);
    }
    void Method(int myRefInt)
    {
        myRefInt += 66;
    }
}

当使用ref关键字的时候,我们传递进函数内部的是我们number的地址,也就是将number的引用传递到函数内部,我们称这种传递参数的方式为引用传递

public class StringBuilder : MonoBehaviour
{
    private void Start()
    {
        int number = 1;
        Method(ref number);
        Debug.Log(number);
    }
    void Method(ref int myRefInt)
    {
        myRefInt += 66;
    }
}

所以我们看到,我们在使用ref的时候,将number的地址传进去了,也采用了number地址上存储的数值1,传出的也是经过函数运算过后的值!这就叫有进有出

当使用out关键字的时候,我们进去的参数就不能进行赋值,而是使用了number,这时候输出的结果就是66,out关键字适用于需要输出多个返回值的类型

int number;

Method(out number);

void Method(out int myRefInt)
{
    myRefInt = 66;
}

Console.WriteLine(number);
//输出:66

13:接口和抽象类的区别?

从具体类→抽象类→接口:是一个不断抽象的过程,实现的越少,抽象的就越多

抽象类是未完全实现的类,它的部分功能推迟到子类去实现,它是专门为复用而生

接口是完全未实现的“类”,它约束了类的基本功能,便于类的分类和扩展,接口定义了语法合同“是什么”部分,派生类定义了语法合同“怎么做”部分,子类必须实现!

接口中可以定义属性,方法,事件

public class InterfaceTest : MonoBehaviour
{
    private void Start()
    {
        MyClass myClass = new();
        MySecongClass mySecongClass = new();

        TestInterface(myClass);
        TestInterface(mySecongClass);
    }
    /// <summary>
    /// 构建参数类型为接口的函数,就可以实现其中的功能
    /// </summary>
    /// <param name="myInterface"></param>
    private void TestInterface(IMyInterface myInterface)
    {
        myInterface.Testfunction();
    }
}
/// <summary>
/// Interface默认是public
/// </summary>
interface IMyInterface
{
    /// <summary>
    /// 默认接口所有元素都是公开的
    /// </summary>
    void Testfunction();
    //{
    //    Debug.Log("Test Function");
    //}
}
public class MyClass : IMyInterface
{
    /// <summary>
    /// 在子类中,必须得实现接口
    /// </summary>
    public void Testfunction()
    {
        Debug.Log("MyClass.TestFunction()");
    }
}
/// <summary>
/// 实现第二个类
/// </summary>
public class MySecongClass : IMyInterface
{
    public void Testfunction()
    {
        Debug.Log("My Second Class");
    }
}

14:Sealed的作用

在类声明中使用sealed可防止其它类继承此类;

在方法声明中使用sealed修饰符可防止派生类重写此方法。

15:反射的实现原理?

16:.Net和Mono关系?

.Net是微软的一个开发平台,最基础的开发包,最基础的框架

C#开发借助开发工具VS或VSCode

Java可以跨平台,但是C#是通过Mono实现扩平台

17:Func用拉姆达表达式怎么写?

(a,b)=> {};

18:手写冒泡算法

using UnityEngine;
public class BubbleSort : MonoBehaviour
{
    private void Start()
    {
        int[] array = { 6, 5, 8, 7, 1, 2, 3, 5 };
        Sort(array);
        for (int i = 0; i < array.Length; i++)
        {
            Debug.Log(array[i]);
        }
    }

    private void Sort(int[] array)
    {
        //进行i次排序,对数组内所有元素都进行比较
        for (int i = 0; i < array.Length - 1; i++) 
        {
            //对某一元素进行的相邻元素的比较,比较次数差i次
            for(int j = 0; j < array.Length-1-i; j++)
            {
                if(array[j] > array[j+1])
                {
                    int temp = array[j];
                    //如果左边的数字比右边的大,就把大的数字向右平移一位
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }
}

19:C#中有那些容器及区别?

Stack:先进后出,入栈和出出栈

Queue:先进先出,入队和出队

Array:提前声明长度,不安全

ArrayList:动态增加数组,不安全

List:底层是泛型数组,动态扩充,安全

LinkList:链表,很好的解决了数组插入效率低的问题,但是访问效率比较低

HashTable哈希表和Dictionary

public class HashTableTest : MonoBehaviour
{ 
    static void Main(string[] args)
    {
        Dictionary<string, string> dic = new Dictionary<string, string>();
        Hashtable hashtable = new Hashtable();
        hashtable.Add("1", "雷");
        hashtable.Add("2", "李");

        dic.Add("一", "一");
        dic.Add("二", "二");
        foreach(var item in dic.Keys)
        {
            Debug.Log(dic[item]);
        }
    }
}

哈希表和字典在内容上比较相似,不同的是字典不需要装箱拆箱,在添加效率上高

不同的是,字典中键值对类型取决于定义字典时的类型设置,而哈希表不限制类型数量

20:常规容器和泛型容器的区别?

不带泛型的容器需要装箱拆箱,比如HashTable,ArrayList

带泛型则不需要,比如List,Dictionary

21:有哪些常见的数值类

简单的数值类:整形,实数,字符类型,布尔类型

复合值类型:结构类型,枚举类型

22:JIT和AOT区别?

JIT全程Just In Time,AOT全程Ahead Of Time

前者是运行时编译,后者是运行前编译

JIT可以根据当前硬件情况实时编译生成最优机器码,但是占用资源导致卡顿

AOT可以避免在运行时编译的内存消耗,缺点是在运行前编译会占用更多安装时间

23:委托和事件的区别

委托可以把一个方法带入,相当于函数指针

而事件是基于委托的,public event EventHandler Order3

每次可以通过+=订阅事件或委托

触发委托:委托.Invoke,委托实例(new)

即:myDelegate.Invoke()和MyDelegate myDelegate = new MyDelegate(Function);

24:结构体和类有什么区别?

两者都是Continer类型,都可以包含其它数据类型成员,包括构造函数,方法,属性,字段,常量,事件,以及处理事件的函数

两者都能实现接口

两者都能声明和触发事件,都能声明委托

区别:

结构体是值类型,类是引用类型

值类型用于存储数据的值,而引用类型用于存储数据的引用

结构体使用栈存储,类使用堆存储

所有结构体默认是Public,而类默认为Private

结构体不能被声明为Protected,而类可以

结构体变量声明不能使用New,而类可以

结构体没有析构函数,而类有

结构体无法继承,而类可以

25:字典的内部实现原理

泛型集合命名空间using system.Collection.Generic

任何键都是唯一的

原理是哈希算法:将不定长度的二进制数据集给映射到一个较短的二进制长度数据集一个Key通过HashFunc得到一个HashCode

26:泛型是什么?

从下面的例子可以得到一些启发

using UnityEngine;

public class GenericTest : MonoBehaviour
{
    /// <summary>
    /// 实例化泛型类
    /// </summary>
    GenericT<string> myClass = new GenericT<string>();
    Hero hero = new Hero();
    private void Start()
    {
        //调取泛型类中的数据
        myClass.Data = "a";
        APrint(1);
        APrint("a");
        APrint(hero);
    }
    void APrint<T>(T input)
    {
        Debug.Log(input);
    }
    
}
public class Hero
{

}
/// <summary>
/// 声明一个泛型类,里面的数据可以是任何类型的
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericT<T> {
    public T Data;
}

我们可以自定义一个泛型类或者泛型方法,就是在定义时候不规定它能存储的数据类型,而在实现的时候去写,你可以把它理解成类的模板

泛型不强制进行装箱拆箱,所以性能得到了提高

27:Mathf.Round,Mathf.Clamp,Mathf.Lerp区别?

Mathf.Round:四舍五入

Mathf.Clamp:左右限值

Mathf.Lerp:插值

28:四元数Quaternion的作用,四元数对欧拉角的优点

四元数用于旋转,相比欧拉角能进行增量旋转,避免万向锁

29:MVC是什么?

Model、View、Controller,是一种软件设计规范,是将业务逻辑、数据、显示分离的方法来组织代码的架构

它主要用于降低视图与业务逻辑间的双向耦合

30:Unity协程是什么,有什么作用?优缺点

Coroutine

协程由协程函数和协程调度器两部分构成

startCoroutine(enumerator)
public class IEnumeratorTest : MonoBehaviour
{
    IEnumerator enumerator()
    {
        Debug.Log("1");
        yield return new WaitForSeconds(1);
    }
}

Ienumerator就是我们定义的协程函数

yield就是我们返回条件

一个程序在执行的过程中,在任意位置使用yield语句,yield返回值控制何时向下执行

它在性能上没有更多开销,但是它不是真正的多线程,会堵塞

31:C#中的Thread是什么?

在使用C#的时候,可以使用Thread线程,它位于System.Threading名称空间中

在Socket网络中使用较多

它是多线程的,而协程是Unity专属的

32:碰撞器和触发器的区别?

碰撞器是触发器的载体

当is Trigger=false,碰撞器根据物理引擎发生碰撞,可以调用OnCollisionEnter/Exit

当is Trigger=true,碰撞器被物理引擎忽略,可以调用OnTriggerEnter/Exit

public class TriggerTest : MonoBehaviour
{
    private void OnCollisionEnter(Collision collision)
    {
        Debug.Log("1");
    }
    private void OnTriggerEnter(Collider other)
    {
        Debug.Log("2");
    }
}

33:物体发生碰撞的必要条件?

两个物体都要有碰撞体Collider,其中一个物体要有刚体RigidBody

34:Onenable,awake,start执行顺序?

Awake → OnEnable → Start

35:CharacterController和RigidBody的区别

RigidBody是完全真实的物理特性,而CharacterController是受限的RigidBody

36:移动相机动作在哪个函数里?为什么?

LateUpdate,在所有Update调用完成后才调用

37:物理更新一般放在哪个系统函数中?

FixedUpdate,和Update比,它是渲染帧执行,每帧执行

38:有哪几种施加力的方式?

rigidbody.AddForce

rigidBody.AddForceAtPosition

39:TexturnType选为Texture和Sprite区别?

Texture是作为模型贴图使用,Sprite作为UI精灵使用

40:如何使用u3d制作2d游戏?有几种方式?

1:使用自身的UGUI

2:把相机投影改为正交,不考虑Z(Projection改成Orthographic)

3:使用2D ToolKit插件

41:MeshRender中material和sharedmaterial的区别

sharedmaterial是改变所有使用该材质的物体,并且也改变存储在工程中的材质设置

应该使用material

42:在物体发生碰撞的整个过程中,有哪几个阶段?列出函数

OnCollisionEnter、OnCollisionStay、OnCollisionExit

43:什么是渲染管道?

渲染管道是指在显示器上为了显示图像而经过的一系列操作

渲染管道中有很多操作步骤,都要将几何物体从一个坐标系变换到另一个坐标系

本地坐标→视图坐标→背面裁剪→光照→裁剪→投影→视图变换→光栅化

44:顶点着色器和像素着色器是什么?

(Vetex Shader)顶点着色器是一组指令代码,这组指令代码在顶点被渲染时执行,它可以提高渲染速度

像素着色器也是一组指令代码,这组指令代码是在顶点中像素被渲染时执行

45:射线检测Raycast原理

从一个起点向某一个方向发射一条射线,返回碰撞到物体的碰撞信息

using UnityEngine;

public class RayCastTest : MonoBehaviour
{ 
    private void Update()
    {
        //声明一个射线
        Ray ray = new Ray(transform.position,transform.forward);

        //声明一个结构体用来存储碰撞信息
        RaycastHit hitInfo;

        if(Physics.Raycast(ray,out hitInfo))
        {
            Debug.Log(hitInfo.collider.gameObject.name);
        }
    }
}

声明射线,声明射线结构体

用Physics里的Raycast方法

46:image和Rawimage区别?

image比rawimage更消耗性能

image只能使用Sprite属性的图片,而RawImage都可以

RawImage适合放单独展示的照片

47:什么是链条关节

Hinge Joint,模拟两个物体之间用 一根链条连接在一起的情况,保持两个物体在一个固定距离

48:游戏动画有几种?

关节动画:把角色分成若干独立部分,一个部分对应一个网格模型

骨骼动画:按角色特点组成一定的层次结构,有关节相连

单一网格模型动画:比较真实

49:写出Animation的五个方法?

AddClip:将Clip添加到动画中

RemoveClip

IsPlaying

Stop

CrossFade:淡入淡出

50:alpha blend工作原理?

Alpha Blend实现透明效果,不过只能针对某块区域进行alpha操作

51:LOD是什么,优缺点是什么?

Level Of Detail:多层次细节

按照模型的位置和重要程度决定物体渲染的资源分配,降低非重要物体的面数和细节度

52:DrawCall是什么,如何降低?

每次引擎准备数据并通知GPU的过程被称为一次Draw Call,该数值越大,显卡消耗越大

Dynamic Batching

Static Batching

高级特性Shader降为统一低级特性Shader

53:MipMap是什么,作用?

在三维贴图渲染中常用的技术,为了加快渲染进度和减少图像的锯齿,贴图被处理成由一系列被预先计算和优化过的图片组成的文件,这样的贴图被称为MipMap

54:FSM有限状态机?

是一种数据结构

55:如何让已经存在的游戏物体在LoadLevel后不被卸载?

Awake()
{
    DontDestroyOnLoad(transform.gameobject);
}

56:Prefabs的好处

在游戏运行时实例化,它相当于模板,对已有的素材,脚本,参数做一个默认的配置

57:简单制作移动方法WSAD,跳跃,旋转

在Edit→InputManager中设置前后左右

Horizontal是水平轴,对应键盘的AD

public class Movement : MonoBehaviour
{
    public float speed = 10;
    private Rigidbody2D rb;
    private void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }
    private void FixedUpdate()
    {
        MovementPlay();
    }
    void MovementPlay()
    {
        float horizontalmove = Input.GetAxisRaw("Horizontal");
        if(horizontalmove != 0)
        {
            //因为在2D中,所以只需要Vector2的移动,参数是
            rb.velocity = new Vector2(speed * horizontalmove, rb.velocity.y);
        }
    }
}

58:场景加载

同步加载:

打包好场景,写代码

using unityEngine.SceneManagerment;

start()
{
    Scenemanager.LoadScene(1,LoadSceneMode.Additive);
}

异步加载:让玩家等待加载

使用协程和asyncOperation完成

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class loading : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(Load());
    }


    IEnumerator Load()
    {

        AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(1, LoadSceneMode.Additive);

        asyncOperation.allowSceneActivation = false;    // 这里限制了跳转


        // 这里就是循环输入进度
        while(asyncOperation.progress < 0.9f)
        {
            Debug.Log(" progress = " + asyncOperation.progress);
        }

        asyncOperation.allowSceneActivation = true;    // 这里打开限制
        yield return null;

        if(asyncOperation.isDone)
        {
            Debug.Log("完成加载");
        }
    }

    // Update is called once per frame
    void Update()
    {
      
    }
}

59:unity常用资源路径有哪些?

1:Application.dataPath                                   Assets目录

2:Application.streamingAssetsPath                Assets/流资源目录

3:Application.persistentDataPath                   持久化目录

4:Resources                                                    包内相对路径

60:unity提供了一个用于保存数据的类,(playerprefs),请列出保存读取整形数据的函数

public class playerPrefbs : MonoBehaviour
{
    private string username;
    void SetPrefs()
    {
        PlayerPrefs.SetString(username, "ss");
    }
}

PlayerPrefs支持三种数据类型的保存和读取,浮点,整形,字符串

对应SetInt,SetFloat,SetString

61:动态加载资源的方式?

Instantiate:适用于简单的动态实例化

public GameObject Player;
Public Transform pos;

void Create()
{
    instantiate(Player,pos,identity);
}

AssetsBundle:将资源打包成AB,放在服务器或者本地磁盘

Resources.Load:可以直接load并返回某个类型的object

一般来说,unity有个特殊的文件夹,放在这个文件夹下的资源可以通过Resorces.Load()加载

62:如何安全的在不同的工程之间迁移asset数据,三种方法

1:将Assets目录和Library一起迁移

2:导出包

3:用unity自带的assets Server功能

63:AB包打包?AB包加载?AB包卸载?

1):打包

将资源设置成预制体

在预制体下方有AssetBundle,左边设置包名称,右边设置扩展名

接着通过代码打包

using System.IO;
using UnityEditor;
public class AssetsBundleTest
{
    //扩展编辑器
    [MenuItem("Assets/CreateBundle")]
    static void CreateAssetBundle()
    {
        string dir = "AssetBundle";//写入文件
        //如果没有dir文件夹
        if (!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }
        //path:打包路径,BuildAssetBundleOptions.None:打包方式, BuildTarget.StandaloneWindows64:打包的目标平台
        BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
    }
}

2)加载

第一种:通过协程在内存读取(Load From Memory)

IEnumerator Start()
{
  string path = "AssetBundles/cube.unity3d";

  AssetBundleCreateRequest reques = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));

  yield return request;
  
  AssetBundle ab = request.assetBundle;
  
  GameObject wallPrefab = ab.LoadAsset<GameObject>("Cube");
  
  Instantiate(wallPrefab);
}

第二种:通过协程在文件读取(LoadFromFileAsync)

IEnumerator Start()
{
string path = "AssetBundles/wall.unity3d";

  AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path);

  yield return request;

  AssetBundle ab = request.assetBundle;

  GameObject wallPrefab = ab.LoadAsset<GameObject>("Cube");

  Instantiate(wallPrefab);
}

第三种:通过WebRequest

IEnumerator Start()
{

  string uri = @"http://localhost/AssetBundles/cubewall.unity3d";

  UnityWebRequest request =   UnityWebRequest.GetAssetBundle(uri);

  yield return request.Send();

  AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);

  GameObject wallPrefab = ab.LoadAsset<GameObject>("Cube");

  Instantiate(wallPrefab);
}

第四种:通过WWW(LoadFromCacheOrDownload)

private IEnumerator LoadNoDepandenceAsset()
    {
        string path = "";
        
        if (loadLocal)
        {
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
            path += "File:///";
#endif
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
            path += "File://";
#endif
            path += assetBundlePath + "/" + assetBundleName;
            
            //www对象
            WWW www = new WWW(path);
 
            //等待下载【到内存】
            yield return www;
 
            //获取到AssetBundle
            AssetBundle bundle = www.assetBundle;
 
            //加载资源
            GameObject prefab = bundle.LoadAsset<GameObject>(assetRealName);
 
            //Test:实例化
            Instantiate(prefab);
        }

3)卸载

AssetBundle.Unload(bool),True卸载所有资源

64:Lua如何调用C#

1:Resources文件夹下的Lua文件,使用Lua的Require函数即可

2:如果Lua文件是下载的,使用自定义Loader即可

65:热更新方案有哪些?

1:整包:存放在StreamingAssets下

2:分包:少部分资源放在包里,大部分资源放在更新资源器中

3:文件可读写路径

66:客户端和服务器交互方式有哪几种?

Socket,主要有UPD和TCP两种协议

协议传输主要有基于http协议的Soap协议

68:UDP和TCP含义,区别

UPD是用户数据包协议

TCP是传输控制协议

69:shader分哪几种,有什么区别?

表面着色器

顶点片段着色器

固定功能管线着色器


70:unity在移动设备上的一些优化资源的方法?

1:使用AB包,实现资源分离和共享

2:降低顶点数在8w以下

3:只使用一盏动态光,不使用阴影,不使用光照探针

4:裁剪粒子特效

5:去掉法线计算

6:删除无意义的animator

..

71:CPU优化小知识?

1:逻辑和表现尽可能分开

2:减少C#与Lua的频繁交互

3:使用StringBuilder实现字符串拼接

4:删除非必要的功能函数,特别是Update这种

...

72:GPU优化小知识?

合理规划好渲染顺序,避免不必要的overdraw

分辨率缩放

73:使用过哪些unity插件?

A* Pathfinding

Qhierchary

ShaderGraph

cinemachine+timeline

DoTween Pro

TextMeshPro

Easytouch

Recorder

Xchart

CurvedUI

VRTK

SteamVR

74:Unity中的监听器?

.?Invoke

AddListener

=>

单例模式

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码

)">
< <上一篇
下一篇>>