Unity面經總結


AssetBundle游戲資源的打包

定義:AssetBundle是unity的一種資源包文件,我們可以將游戲中使用到的游戲資源打包成Assetbundle,在游戲運行時動態加載游戲資源。

動態加載Assetbundle,以及加載過程

  • 使用靜態方法Assetbundle.LoadFromFile將Assetbundle文件從硬盤加載到內存中
  • 之后通過獲取到的Assetbundle變量,使用Assetbundle.LoadAsset方法獲取Assetbundle中的資源
  • 最后通過Instantiate方法實例化出對象
  • 優點:可以將資源分塊加載,將相關聯的資源打包成一個Assetbundle,便於資源的管理。
  • 缺點:在每次資源更新之后都需要重新打包。

Resource.Load動態加載,及加載過程

  • 可以使用Resources.Load方法加載資源,資源需要放在unity默認的Resources目錄中,但在游戲實際開發中並不建議使用。
  • Resources.UnloadUnusedAssets() 可以清理內存,可以卸載內存中的GameObject,會把沒有使用(或者資源=null)的資源清理掉。
  • Resources.UnloadAsset卸載單個資源,只能卸載單個基礎資源(如Texture,Sprite),
  • 優點:可以不用打包,將資源放入目錄就可以在運行時加載了。
  • 缺點:Resources目錄無法動態更新,所以在游戲發布和版本升級上不適用。

C#中裝箱,拆箱

裝箱是讓一個值類型隱式的轉換為引用類型,從任何值類型到object類型; 拆箱是讓一個引用類型顯式的轉換為一個值類型,從object類型到任何值類型 

“箱”指的就是托管堆,裝箱即指在托管堆中將在棧上的值類型對象封裝,生成一份該值類型對象的副本,並返回該副本的地址。而拆箱即是指返回已裝箱值類型在托管堆中的地址

裝箱的過程為:
  1. 分配內存: 在托管堆中分配好內存,內存的大小是值類型的各個字段需要的內存量加上托管堆的所有對象都有的兩個額外成員—類型對象指針和同步塊索引—所需要的內存量之和。
  2. 復制對象: 將值類型的字段復制到新分配的內存中。
  3. 返回地址: 將已裝箱的值類型對象的地址返回給引用類型的變量。
拆箱的過程為:
  1. 檢查實例:首先檢查變量的值是否為null,如果是則拋出NullReferenceException異常;再檢查變量的引用指向的對象是不是給定值類型的已裝箱對象,如果不是,則拋出InvalidCastException異常。
  2. 返回地址:返回已裝箱實例中屬於原值類型字段的地址,而兩個額外成員(類型對象指針和同步塊索引)則不會返回

C#值類型,引用類型

值類型直接存儲其值,而引用類型存儲對其值的引用。 

值類型:

派生自System.ValueType:byte,short,int,long,float,double,decimal,char,bool 和 struct

作為局部變量時,存儲在棧上

引用類型:

基類為Objcet:string 和 class

引用類型在棧中存儲一個引用,其實際的存儲位置位於托管堆

數組的元素不管是引用類型還是值類型,都存儲在托管堆上。

C#的struct和class的區別

  • 結構體是值類型,類是引用類型:類在傳遞的時候,傳遞的內容是位於托管內存中的位置,結構體在傳遞的時候,傳遞的內容是位於程序堆棧區的內容。當類的傳遞對象修改時,將同時修改源對象,而結構體的傳遞對象修改時,不會對源對象產生影響。
  • 結構體不能被繼承,類完全可擴展的,可以繼承其他類和接口,自身也能被繼承,除非用sealed
  • 結構不能包含顯式默認構造函數;沒有析構函數,沒有abstract和sealed(因為不能繼承);不能有protected修飾符,可以不使用new初始化。而類有默認的構造函數,有析構函數,可以使用abstract和sealed,有protected修飾符,必須使用new初始化。

unity3d的生命周期

Awake()(當腳本創建的時候調用,僅一次) -> onEnable()(當對象變為可用和激活時調用) -> Start() : (在update方法第一次調用前調用) -> FixedUpdate() (是按照固定時間而非幀率更新的, 多用於物理計算和更新) -> Update() (每一幀時調用) -> LateUpdate() (攝像機跟隨的更新) -> OnGUI() (繪制GUI時調用) -> OnDisable() (當對象變為不可用和不激活時調用) -> OnDestory() (當對象銷毀時調用)

區別:

  • Update() : 大部分游戲行為代碼被執行的地方, 是在每次渲染新的一幀的時候才會調用,與當前平台幀數有關, 一般放畫面控制邏輯
  • FixedUpdate(),是在固定的時間間隔執行,不受游戲幀率的影響, 常用於物理相關的計算, 對Rigidbody的操作
  • LateUpdate() 是在所有Update函數調用后被調用,一般放攝像機跟隨的更新。所有的update() 操作完才進行攝像機的跟進,不然就有可能出現攝像機已經推進了,但是視角里還未有角色的空幀出現。

unity中的碰撞器和觸發器的區別

  • 在collider組件上有屬性IsTrigger來選擇是否為觸發器
  • 碰撞器彼此之間不會進入對方,而觸發器可以
  • 當Is Trigger=false時,碰撞器根據物理引擎引發碰撞,碰撞器觸發事件時會調用OnCollisionEnter->OnCollisionStay->OnCollisionExit函數。
  • 當Is Trigger=true時,碰撞器被物理引擎所忽略,觸發器觸發事件時會調用OnTriggerEnter->OnTriggerStay->OnTriggerExit函數。

unity的光源有幾種類型,分別是什么

  • Directional Light:平行光源,如同現實中的太陽光源
  • Point Light:點光源,如果現實中的蠟燭
  • Spot Light:聚光燈光源,如同現實中的手電筒
  • Area Light:區域光源

Coroutines(協同程序)

在主線程運行的同時開啟另一段邏輯處理,來協助當前程序的執行,協程很像多線程,但是不是多線程。Unity的協程在每幀結束之后去檢測yield的條件是否滿足。

lightmap,有什么作用

lightmap就是事先烘焙好的光照貼圖,這樣可以避免使用實時光照,可以減少Drawcall提高性能。在三維軟件里實現打好光,然后渲染把場景各表面的光照輸出到貼圖上,最后又通過引擎貼到場景上,這樣就使物體有了光照的感覺。

ref參數和out參數是什么?有什么區別?

ref和out參數的效果一樣,都是通過關鍵字找到定義在主函數里面的變量的內存地址,並通過方法體內的語法改變它的大小。

不同點:

  • 使用ref型參數時,傳入的參數必須先被初始化。對out而言,必須在方法中對其完成初始化。
  • out適合用在需要retrun多個返回值的地方,而ref則用在需要被調用的方法修改調用者的引用的時候。
  • ref參數是引用,out參數為輸出參數。

RawImage和Image的區別

  1. RawImage可以顯示任何類型的Texture,而Image只能顯示Sprite
  2. RawImage支持UV Rect,Image支持Simple(簡單)/Sliced(切片)/Tiled(平鋪)/Filled(填充)

前向渲染 v.s 延后渲染

前向渲染 (Forward Rendering):把空間的點進行各種剪裁后,進行處理。先渲染一遍物體,把法線和高光存在ARGB32的渲染紋理中(法線用rgb通道,高光用a通道),存在了z buffer里;然后通過深度信息,法線和高光信息計算光照(屏幕空間),光照信息緩存在Render Texture中;最后混合。

延后渲染 (Deferred Rendering):將攝像機空間的點光柵化轉化成屏幕坐標后再進行處理。先不進行光照運算,對每個像素生成一組數據(G-buffer),包括位置,法線,高光等,然后用這些數據將每個光源以2D后處理的方式施加在最后圖像上(屏幕空間),然后只進行了一次光照計算就實現了最終效果。缺點:不適用於半透明。

C#的GC

原理:程序創建出來的對象實例都會被CLR跟蹤,CLR都是有記錄哪些對象還會被用到(存在引用關系);哪些對象不會再被用到(不存在引用關系)。CLR會整理不會再被用到的對象,在恰當的時機,按一定的規則銷毀部分對象,釋放出這些對象所占用的內存。

優點:

  1. 提高了軟件開發的抽象度;
  2. 程序員可以將精力集中在實際的問題上而不用分心來管理內存的問題;
  3. 可以使模塊的接口更加的清晰,減小模塊間的偶合;
  4. 大大減少了內存人為管理不當所帶來的Bug;
  5. 使內存管理更加高效。

避免:

  1. 減少new產生對象的次數
  2. 使用公用的對象(靜態成員)
  3. 將String換為StringBuilder. String類型是個不可變的對象,當每次對String進行改變時都需要生成一個新的String對象,然后將指針指向一個新的對象,如果在一個循環里面,不斷的改變一個對象,就要不斷的生成新的對象,所以效率很低,建議在不斷更改String對象的地方不要使用String類型。StringBuilder對象在做字符串連接操作時是在原來的字符串上進行修改,改善了性能.

C#的接口和抽象類的區別

使用抽象類是為了代碼的復用,而使用接口的動機是為了實現多態。

相同點:

  • 都可以被繼承
  • 都不能被實例化
  • 都可以包含方法聲明
  • 派生類必須實現未實現的方法

區 別:

  • 抽象基類可以定義字段、屬性、方法實現。接口只能定義屬性、索引器、事件、和方法聲明,不能包含字段。
  • 抽象類是一個不完整的類,需要進一步細化,而接口是一個行為規范。微軟的自定義接口總是后帶able字段,證明其是表述一類“我能做。。。”
  • 接口可以被多重實現,抽象類只能被單一繼承
  • 抽象類是從一系列相關對象中抽象出來的概念, 因此反映的是事物的內部共性;接口是為了滿足外部調用而定義的一個功能約定, 因此反映的是事物的外部特性
  • 接口基本上不具備繼承的任何具體特點,它僅僅承諾了能夠調用的方法    
  • 抽象類實現的具體方法默認為虛的,但實現接口的類中的接口方法卻默認為非虛的,當然您也可以聲明為虛的 

什么是Drawcall,如何優化Drawcall

定義:引擎每一次對GPU的調用的過程叫做Drawcall,每一個網格(Mesh)使用一個不同的材質(Material)將需要一個單獨的Draw Call。

優化:

  1. 將相關的零散圖片資源放到同一個Sprite中,這樣多次Drawcall調用機會合並成一次
  2. 將使用同一圖集的對象排序在一起,使用同一圖集的對象會被一起繪制以減少Drawcall。

DrawCall多少為好:150以下

Animator里面的狀態機

Animator主要包含一個狀態機和Avatar, 狀態機里管理者各個動畫(Animation)的切換條件

  1. 在場景(Scene)內制造一個GameObject,然后加上Animator組件。
  2. 在項目文件夾內制造一個AnimatorController並把它鏈接到上述Animator中。
  3. 雙擊 Animator,會出來一個 Animator 面板,拖入三個動作作為狀態(第一個拖入的作為默認狀態,不過可以右鍵 Default State 設置其他狀態為默認狀態)
  4. 右鍵 AnyState,Make Transition 連接到三個狀態
  5. 添加狀態控制參數 AnimState, 編輯切換狀態的條件
  6. 取消勾選 Can Transition To Self,不然動畫會出現抖動

Animator v.s. Animation

  • Animation 控制一個動畫的播放,而Animator是多個動畫之間相互切換,並且Animator 有一個動畫控制器,俗稱動畫狀態機。
  • Animation只能對UI組件執行動畫,但Animator幾乎可以對任何對象執行動畫
  • Animation只有透明度,旋轉,平移,伸縮4中屬性,而Animator,只要是該控件的屬性,且有setter該屬性的方法就都可以對該屬性執行一種動態變化的效果

static,const,readonly getonly的區別

  • const:定義的是靜態常量在對象初始化的時候賦值,以后不能改變它的值,屬於編譯常量
  • readonly:只讀變量,只能用來修飾類字段,不能修飾局部變量, 屬於運行時變量, 可能具有不同的值。
  • static:定義的是靜態變量,在程序初始化時被分配,直到程序退出前才被釋放。
  • statc readonly: 在運行時計算出其值的, 能通過靜態構造函數來賦值。

什么是反射

反射提供了封裝程序集、模塊和類型的對象(Type 類型)。可以使用反射動態創建類型的實例,將類型綁定到現有對象,或從現有對象獲取類型並調用其方法或訪問其字段和屬性。如果代碼中使用了屬性,可以利用反射對它們進行訪問。 Assembly類可以獲得正在運行的裝配件信息,也可以動態的加載裝配件,以及在裝配件中查找類型信息,並創建該類型的實例。
Type類可以獲得對象的類型信息,此信息包含對象的所有要素:方法、構造器、屬性等等,通過Type類可以得到這些要素的信息,並且調用之。
MethodInfo包含方法的信息,通過這個類可以得到方法的名稱、參數、返回值等,並且可以調用之。

如何在不同分辨率下保持UI的一致性

NGUI可以實現屏幕分辨率的自適應性,原理就是計算出屏幕的寬高比跟原來的預設的屏幕分辨率求出一個對比值,然后修改攝像機的size。UGUI通過錨點和中心點和分辨率也解決這個問題

委托是什么?事件是不是一種委托?

委托類似於一種安全的指針引用,在使用它時是當做類來看待而不是一個方法,相當於對一組方法的列表的引用。類似於c或c++中的函數的指針,但函數指針只能引用靜態方法,而委托既能引用靜態方法,也能引用實例方法。委托可以把一個方法作為參數代入另一個方法。事件是一種特殊的委托 事件的實現依賴於委托

Unity中,照相機的Clipping Planes的作用是什么

剪裁平面 。Near、Fare兩個值是從相機到開始渲染和停止渲染之間的距離

簡述Unity3D項目中預制組件上出現數據丟失的原因可能有哪些?

一般是組件上綁定的物體對象被刪除了

Unity3D中的協程(coroutine)和C#線程之間的區別是什么

多線程程序同時運行多個線程 ,而在任一指定時刻只有一個協程在運行,並且這個正在運行的協同程序只在必要時才被掛起。除主線程之外的線程無法訪問Unity3D的對象、組件、方法。

請簡述ArrayList和List的主要區別?

ArrayList會把所有插入其中的數據都當做Object來處理,裝箱拆箱的操作(費時),List是泛型類,功能跟ArrayList相似,但不存在ArrayList所說的問題。

簡述一下對象池,你覺得在FPS里哪些東西適合使用對象池

對象池就存放需要被反復調用資源的一個空間。當一個對象回大量生成的時候如果每次都銷毀創建會很費時間,通過對象池把暫時不用的對象放到一個池中(也就是一個集合),當下次要重新生成這個對象的時候先去池中查找一下是否有可用的對象,如果有的話就直接拿出來使用,不需要再創建,如果池中沒有可用的對象,才需要重新創建,利用空間換時間來達到游戲的高速運行效果,在FPS游戲中要常被大量復制的對象包括子彈,敵人,粒子等。

簡述prefab的用處

在游戲運行時實例化,prefab相當於一個模板,對已經有的素材、腳本、參數做一個默認的配置,以便於以后的修改,同時prefab打包的內容簡化了導出的操作,便於團隊的交流。

目錄作用

  • Plugins:擴展unity功能的插件
  • Editor:主要用來擴展unity編輯器的功能
  • Resources:動態加載游戲資源到場景中
  • StreamingAssets:存放需要保留原格式的資源

卡頓怎么解決,比如背包系統有3000個物品,用戶操作會很卡,怎么解決

C#中四種訪問修飾符是哪些?各有什么區別?

1. 屬性修飾符:

  • Serializable:按值將對象封送到遠程服務器。
  • STATread:是單線程套間的意思,是一種線程模型。
  • MATAThread:是多線程套間的意思,也是一種線程模型。

2. 存取修飾符:

  • public:存取不受限制。
  • private:只有包含該成員的類可以存取。
  • internal:只有當前工程可以存取。
  • protected:只有包含該成員的類以及派生類可以存取。

3. 類修飾符:

  • abstract:抽象類。指示一個類只能作為其它類的基類。
  • sealed:密封類。指示一個類不能被繼承。

4. 成員修飾符:

  • abstract:指示該方法或屬性沒有實現。
  • sealed:密封方法。可以防止在派生類中對該方法的override(重載)。不是類的每個成員方法都可以作為密封方法密封方法,必須對基類的虛方法進行重載,提供具體的實現方法。所以,在方法的聲明中,sealed修飾符總是和override修飾符同時使用。
  • delegate:委托。用來定義一個函數指針。C#中的事件驅動是基於delegate + event的。
  • const:指定該成員的值只讀不允許修改。
  • event:聲明一個事件。
  • extern:指示方法在外部實現。
  • override:重寫。對由基類繼承成員的新實現。
  • readonly:指示一個域只能在聲明時以及相同類的內部被賦值。
  • static:指示一個成員屬於類型本身,而不是屬於特定的對象。即在定義后可不經實例化,就可使用。
  • virtual:指示一個方法或存取器的實現可以在繼承類中被覆蓋。
  • new:在派生類中隱藏指定的基類成員,從而實現重寫的功能。 若要隱藏繼承類的成員,請使用相同名稱在派生類中聲明該成員,並用 new 修飾符修飾它。

 

參考資料:

https://blog.csdn.net/Fenglele_Fans/article/details/80178463


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM