文章目錄
- 18.1 皮皮收到面試邀請
- 18.2 面試題庫相關網站
- 18.2.1 牛客網
- 18.2.2 領扣LintCode
- 18.2.3 力扣LeetCode
- 18.3 優質學習網站
- 18.3.1 菜鳥教程
- 18.3.2 W3CSCHOOL
- 18.3.3 BiliBili
- 18.3.4 中國大學MOOC
- 18.3.5 我要自學網
- 18.4 程式員問答社區
- 18.4.1 CSDN
- 18.4.2 博客園
- 18.4.3 知乎
- 18.4.4 掘金
- 18.4.5 思否
- 18.4.6 開源中國
- 18.4.7 InfoQ
- 18.5 Unity3D面試題樣題
- 一 判斷題
- 二 填空題
- 三 問答題
- 四 場景題
簡介:我是一名
Unity游戲開發工程師,皮皮是我養的貓,會講人話,它接到了喵星的特殊任務:學習編程,學習
Unity游戲開發,
于是,發生了一系列有趣的故事,
18.1 皮皮收到面試邀請
皮皮的簡歷很快得到了回復,
【XXX企業】
尊敬的皮皮貓先生,
歡迎您應聘我公司Unity3D游戲開發職位,經過初步審核覺得您比較適合我們公司,邀請您下周一上午10:00參加面試,
收到請回復,謝謝!
我:“面試不可怕,提前做好準備,放平心態,一定可以的,”
皮皮:“我沒有害怕呀,小場面,Hold得住,”
我:“不要大意,我推薦一些對你有幫助的網站給你,你這兩天好好準備準備,”
18.2 面試題庫相關網站
18.2.1 牛客網
網址:https://www.nowcoder.com/
找作業神器,筆試題庫,面試經驗,新人培訓等,

18.2.2 領扣LintCode
網址:https://www.lintcode.com/
刷題利器,人工智能、大資料、演算法,

18.2.3 力扣LeetCode
網址:https://leetcode-cn.com/leetbook/
海量技術面試資源,幫助你高效提升編程技能,

18.3 優質學習網站
18.3.1 菜鳥教程
網址:https://www.runoob.com/
前后端開發教程都有,非常不錯的學習網站,

18.3.2 W3CSCHOOL
網址:https://www.w3cschool.cn/
全球最大的中文 Web 技術教程,

18.3.3 BiliBili
網址:https://www.bilibili.com/
BiliBili不僅是一個二次元視頻網站,也是一個非常優質的學習網站,上面有非常多優質的學習視頻,
比如搜索python,可以看到很多python的教程,

18.3.4 中國大學MOOC
網址:https://www.icourse163.org/
中國大學MOOC,有豐富的大學視頻教程資源,每個教程的視頻都是一系列的,適合系統性地學習,質量高,而且免費,
比如計算機原理

18.3.5 我要自學網
網址:https://www.51zxw.net/
我要自學網,上面有很多不錯的培訓教程,包括電腦辦公、平面設計、影視影片、機械設計、工業自動、程式設計、網頁設計、會計課程等,

18.4 程式員問答社區
18.4.1 CSDN
網址:https://www.csdn.net/
CSDN是一個專業的開發者社區,在百度上搜索編程相關的問題,都會搜到CSDN的文章,

18.4.2 博客園
網址:https://www.cnblogs.com/
博客園,開發者的網上家園,

18.4.3 知乎
網址:https://www.zhihu.com/
有問題,上知乎,

18.4.4 掘金
網址:https://juejin.im/
掘金,幫助開發者成長的社區,

18.4.5 思否
網址:https://segmentfault.com/
思否,開發者社區和技術媒體,

18.4.6 開源中國
網址:https://gitee.com/oschina
中文開源技術社區,

18.4.7 InfoQ
網址:https://www.infoq.cn/
前沿資訊,潮流技術,文章質量高

18.5 Unity3D面試題樣題
我:“我再出一份樣題給你吧,”
皮皮:“鏟屎官,干脆你直接面試我好了,”
我:“不行,你這是走后門,我不能放水,要靠自己,今天給你加個罐頭,把這份試題完成了就可以吃,”
一 判斷題
1 C#支持繼承多個類,達到重用代碼功能的效果, (×)
2 修改Renderer的sharedMaterial,所有使用這個材質球的物體都會被改變,并且也改變儲存在工程里的材質設定, (√)
3 Unity中可以創建子執行緒,并在子執行緒中直接修改UI物件, (×)
4 Unity不支持在協程中嵌套呼叫協程, (×)
5 C#不同命名空間中可以存在相同類名, (√)
6 Unity會自動為MonoBehaviour子類的public變數做序列化, (√)
7 每個列舉成員均具有相關聯的常數值,可以設定為負數常數, (√)
8 只帶有 get訪問器的屬性稱為只讀屬性,無法對只讀屬性賦值, (√)
9 protected成員只能被本類內部訪問,無法被子類直接訪問, (×)
10 父物體發生Transform變化的時候,子物體跟隨一起變化,但是子物體發生變化的時候,父物體不動, (√)
二 填空題
1 Unity中 Game 視圖可以設定解析度,在該視圖中呈現的就是攝像機渲染的畫面,
2 gameObject.AddComponent<Test>()的時候,Test腳本的 Awake 函式會立即被呼叫,
3 任何游戲物件在創建的時候都會附帶 Transform 組件,用于儲存并操控物體的位置、旋轉和縮放,
4 只在編輯器環境下運行的代碼,可以使用 UNITY_EDITOR 宏把代碼包起來,
5 Unity中可用四元數Quaternion表示 旋轉 ,不受萬向鎖影響,可以進行插值運算,
6 Unity協程中可以使用 yield return null 實作暫緩一幀,在下一幀接著往下處理,
7 transform.forward表示物體的 z 軸的方向,
8 C#中的委托類似于C/C++中的 函式指標 ,委托型別的宣告以 delegate 關鍵字開頭,
9 Unity中的 Plugins 目錄用于放置Native插件檔案,Android平臺的jar檔案必須放置在 Assets/Plugins/Android/libs 目錄中,
10 在移動平臺,Resources目錄中的資源通過 Resources.Load 介面來加載,如果想實作資源增量更新,則一般考慮把資源打包成 AssetBundle 資源型別,
11 定義物件間的一種一對多依賴關系,使得每當一個物件狀態發生改變時,其相關依賴物件皆得到通知并被自動更新,可以使用 觀察者 設計模式,
12 Unity中每個材質球必須系結一個 shader (腳本),它決定了該材質的渲染方式以及可配置屬性,
13 Unity中 StreamingAssets 檔案夾是只讀的,里面的所有檔案將會被原封不動地復制制到目標平臺機器上的特定檔案夾里,不會被壓縮,在Android或iOS平臺,通過 WWW 類來讀取其中的檔案,
14 當場景中有多個攝像機時,可以設定攝像機的 depth 值,調整相機的渲染順序,
15 為了加快渲染速度和減少影像鋸齒,貼圖被處理成由一系列被預先計算和優化過的圖片組成的檔案,這樣的貼圖被稱為 MipMap ,
三 問答題
1 C#中的委托是什么?
答:
delegate int MyDelegate(int value); //宣告委托型別
C#所有的委托派生自System.Delegate類,委托是存有對某個方法的參考的一種參考型別變數,委托變數可以當作另一個方法的引數來進行傳遞,實作事件和回呼方法,有點類似C++中的函式指標,但是又有所不同,在C++中,函式指標不是型別安全的,它指向的是記憶體中的某一個位置,我們無法判斷這個指標實際指向什么,對于引數和回傳型別難以知曉,而C#的委托則完全不同,它是型別安全的,我們可以清晰的知道委托定義的回傳型別和引數型別,
[延伸1]: 關于委托和事件
答:
本質區別:從定義上說,委托被編譯器編譯成一個類,所以它可以像類一樣在任何地方定義,而事件被編譯成一個委托型別的私有欄位和兩個公有add和 remove方法(有點類似于屬性的定義)不過這兩個方法都有一個引數,這個引數就是委托,所以,它只能定義在一個類里面,
event MyDelegate myevent; //定義事件
委托相當于一系列函式的抽象類,這一系列函式要求擁有相同的引數和回傳值;而事件(event)相當于委托的一個實體,事件是委托型別的成員,委托可以定義在類外面,而事件只能定義在類里面,
事件使用 發布-訂閱(publisher-subscriber) 模型,
發布器(publisher) 是一個包含事件和委托定義的物件,事件和委托之間的聯系也定義在這個物件中,發布器(publisher)類的物件呼叫這個事件,并通知其他的物件,
訂閱器(subscriber) 是一個接受事件并提供事件處理程式的物件,在發布器(publisher)類中的委托呼叫訂閱器(subscriber)類中的方法(事件處理程式),
[延伸2]: 為什么需要事件?
答:
事件最常用的應用場景是圖形用戶界面(GUI),如一個按鈕點擊事件,選單選擇事件,檔案傳輸完成事件等,簡單的說,某件事發生了,你必須要作出回應,你不能預測事件發生的順序,只能等事件發生,再作出相應的動作來處理,觸發事件的類本身對怎樣處理事件不感興趣,按鈕說:“我被點過了”,回應類作出合適的回應,
2 值型別與參考型別的區別
答:
1 值型別存盤在堆疊(stack)中,參考型別資料存盤在堆(heap)中,記憶體單元中存放的是堆中存放的地址,
2 值型別存取快,參考型別存取慢,
3 值型別表示實際資料,參考型別表示指向存盤在記憶體堆中的資料的指標和參考,
4 堆疊的記憶體是自動釋放的,堆記憶體是.NET中會由GC來自動釋放,
5 值型別繼承自System.ValueType,參考型別繼承自System.Object,
3 介面Interface與抽象類abstract class的區別
答:
介面和抽象類是支持抽象定義的兩種機制,
介面是完全抽象的,只能宣告方法,而且只能宣告public的方法,不能宣告private及protected的方法,不能定義方法體,也不能宣告實體變數,抽象類是可以有私有的方法或者私有的變數,如果一個類中有抽象方法,那么就是抽象類,
一個類可以實作多個介面,但一個類只能繼承一個抽象類,
介面強調特定功能的實作,具有哪些功能,而抽象類強調所屬關系,
盡管介面實作類及抽象類的子類都必須要實作相應的抽象方法,但實作的形式不同,介面中的每一個方法都是抽象方法,都只是宣告的, 沒有方法體,實作類必須都要實作;而抽象類的子類可以有選擇地實作,只實作其中的抽象方法,覆寫其中已實作了的方法,
[延伸]: 如何榷訓代碼依賴關系?
答:
在代碼的控制流中,呼叫關系和依賴關系幾乎是完全吻合的,如果缺乏良好的封裝與介面提取,那么呼叫者必須掌握被呼叫者的代碼實作,而抽象良好的介面,能夠使控制流對代碼的依賴實作反轉,比如面向同一個介面協議,被呼叫者需要在協議的約束下對提供的服務進行實作,它的代碼依賴協議的制定,而呼叫者只用依據協議按需獲取服務即可,在控制流上依賴介面,而不再需要在代碼上依賴被呼叫者,此即是從介面到被呼叫者的控制流-代碼依賴關系反轉,
代碼依賴關系榷訓,意味著業務可以模塊化、組件化,拆分的功能組團可以以“插件”的方式并行獨立開發維護,這種隔離大大提升開發運維效率,同時獨立部署的能力也更加符合軟硬體發展的趨勢,
4 Unity實作跨平臺的原理
答:
Unity的跨平臺技術是通過一個Mono虛擬機實作的,就是通過Mono將C#腳本代碼編譯成CIL,然后Mono運行時利用JIT或者AOT將CLI編譯成目標平臺的原生代碼實作的,
不過這個虛擬機更新太慢,不能很好地適應眾多的平臺,所以后來推出了IL2CPP,把本來應該再Mono的虛擬機上跑的中間代碼轉換成c++代碼,這樣再把生成的c++代碼,利用c++的跨平臺特性,在各個平臺上通過對各平臺都有良好優化的native c++編譯器編譯,以獲得更高的效率和更好的兼容性,
[延伸1]: 講講你對IL的了解
答:
IL是.NET框架中間語言(Intermediate Language)的縮寫,使用.NET框架提供的編譯器可以直接將源程式編譯為.exe或.dll檔案,但此時編譯出來的程式代碼并不是CPU能直接執行的機器代碼,而是一種中間語言IL(Intermediate Language),
使用中間語言的優點有兩點,一是可以實作平臺無關性,既與特定CPU無關;二是只要把.NET框架某種語言編譯成IL代碼,就實作.NET框架中語言之間的互動操作(這就是為什么unity3D里面可以c#和js混編),
在Mac OS上,因為iOS的現有限制,面向iOS的C#代碼會通過AOT編譯技術直接編譯為ARM匯編代碼,而在Android上,應用程式會轉換為IL,啟動時再進行JIT編譯,
[延伸2]: 講講你對JIT的了解
答:
JIT:即時編譯(Just In-Time compile),這是.NET運行可執行程式的基本方式,編譯一個.NET程式時,編譯器將源代碼翻譯成中間語言,它是一組可以有效地轉換為本機代碼且獨立于CPU的指令,當執行這些指令時,實時(JIT)編譯器將它們轉化為CPU特定的代碼,部分加密軟體通過掛鉤JIT來進行IL加密,同時又保證程式正常運行,JIT也會將編譯過的代碼進行快取,而不是每一次都進行編譯,所以說它是靜態編譯和解釋器的結合體,
5 四元數的作用
答:
四元數用于表示旋轉,
其相對于歐拉角的優點:
1 避免萬向鎖,
2 只需要一個4維的四元數就可以執行繞任意過原點的向量的旋轉,方便快捷,在某些實作下比旋轉矩陣效率更高,
3 可以提供平滑插值,
6 Unity腳本生命周期與執行順序
答:
| 名稱 | 觸發時機 | 用途 |
|---|---|---|
| Awake | 腳本實體被創建時呼叫 | 用于游戲物件的初始化,注意Awake的執行早于所有腳本的Start函式 |
| OnEnable | 當物件變為可用或激活狀態時被呼叫 | |
| Start | Update函式第一次運行之前呼叫 | 用于游戲物件的初始化 |
| Update | 每幀呼叫一次 | 用于更新游戲場景和狀態 |
| FixedUpdate | 每個固定物理時間間隔呼叫一次 | 用于物理狀態的更新 |
| LateUpdate | 每幀呼叫一次(在update之后呼叫) | 用于更新游戲場景和狀態,和相機有關的更新一般放在這里 |
| OnGUI | 渲染和處理OnGUI事件 | |
| OnDisable | 當前物件不可用或非激活狀態時被呼叫 | |
| OnDestroy | 當前物件被銷毀時呼叫 |
[延伸1]: 講講關于Awake與Start的
答:
Awake和Start只會呼叫一次,當游戲程序中調整腳本的可見狀態時,會分別呼叫OnEnable, OnDisable函式,而Awake和Start將不會再呼叫,
Start可能不會被立刻呼叫,比如我們之前沒有讓其enable,當腳本被enable時,Start才會被呼叫,
不同物件之間的Awake順序是不得而知的,
如下,MyBhv腳本在Awake中初始化speed=1f;執行完下面的代碼,speed的值是多少呢?
var bhv = go.AddComponent<MyBhv>()
bhv.speed = 3f;
答案是3f,因為Awake會先執行,
[延伸2]: 講講關于Update與FixedUpdate
答:
同:當MonoBehaviour啟用時,其在每一幀被呼叫,都是用來更新的,
異:Update()每一幀的時間不固定,受場景渲染的復雜程度,還有輸入的一系列事件等等各種原因影響,游戲畫面的幀率是在不斷變化的,
FixedUpdate()每幀與每幀之間相差的時間是相對固定的(值為Time.fixedDeltaTime),默認是0.02s,可以通過Edit->ProjectSettings->Time來設定,物理相關的處理(比如Rigidbody)一般在FixedUpdate()中,
[延伸3]: 講講關于Update與LateUpdate
答:
LateUpdate是在所有Update函式呼叫后被呼叫,可用于調整腳本執行順序,例如當物體在Update里移動時,跟隨物體的相機可以在LateUpdate里實作,
有2個不同的腳本同時在Update中控制一個物體,那么當其中一個腳本改變物體方位、旋轉或者其他引數時,另一個腳本也在改變這些東西,那么這個物體的方位、旋轉就會出現一定的反復,如果還有個物體在Update中跟隨這個物體移動、旋轉的話,那跟隨的物體就會出現抖動, 如果是在LateUpdate中跟隨的話就會只跟隨所有Update執行完后的最后位置、旋轉,這樣就防止了抖動,
7 講講你對Unity的協程的理解
答:
協程不是執行緒,協程的實作原理是迭代器,而迭代器的實作原理是狀態機,
unity中協程執行程序中,通過 yield return XXX,將程式掛起,去執行接下來的內容,在遇到 yield return XXX陳述句之前,協程方法和一般的方法是相同的,也就是程式在執行到 yield return XXX陳述句之后,接著才會執行的是 StartCoroutine()方法之后的程式,走的還是單執行緒模式,僅僅是將 yield return XXX陳述句之后的內容暫時掛起,等到特定的時間才執行,
那么掛起的程式什么時候才執行?協同程式主要是Update()方法之后,LateUpdate()方法之前呼叫的,
通過設定MonoBehaviour腳本的enabled對協程是沒有影響的,但如果gameObject.SetActive(false)則已經啟動的協程則完全停止了,即使在Inspector把gameObject激活還是沒有繼續執行,也就說協程雖然是在MonoBehvaviour啟動的(StartCoroutine),但是協程函式的地位完全是跟MonoBehaviour是一個層次的,不受MonoBehaviour的狀態影響,但跟MonoBehaviour腳本一樣受gameObject控制,也應該是和MonoBehaviour腳本一樣每幀輪詢yield 的條件是否滿足,
協程不是只能做一些簡單的延遲,如果只是單純的暫停幾秒然后在執行就完全沒有必要開啟一個協程,
協程的真正作用是分步做一些比較耗時的事情,比如加載游戲里的資源,
[延伸3]: 講講行程、執行緒、協程
答:
行程擁有自己獨立的堆和堆疊,既不共享堆,亦不共享堆疊,行程由作業系統調度,
執行緒擁有自己獨立的堆疊和共享的堆,共享堆,不共享堆疊,執行緒亦由作業系統調度(標準執行緒是的),
協程和執行緒一樣共享堆,不共享堆疊,協程由程式員在協程的代碼里顯示調度,
協程和執行緒的區別是:協程避免了無意義的調度,由此可以提高性能,但也因此,程式員必須自己承擔調度的責任,同時,協程也失去了標準執行緒使用多CPU的能力,
打個比方吧,假設有一個作業系統,是單核的,系統上沒有其他的程式需要運行,有兩個執行緒 A和 B,A和 B在單獨運行時都需要 10秒來完成自己的任務,而且任務都是運算操作,A B之間也沒有競爭和共享資料的問題,現在A B兩個執行緒并行,作業系統會不停的在 A B兩個執行緒之間切換,達到一種偽并行的效果,假設切換的頻率是每秒一次,切換的成本是0.1秒(主要是堆疊切換),總共需要 20 + 19 * 0.1 = 21.9秒,如果使用協程的方式,可以先運行協程A,A結束的時候讓位給協程 B,只發生一次切換,總時間是20 + 1 * 0.1 = 20.1秒,如果系統是雙核的,而且執行緒是標準執行緒,那么A B兩個執行緒就可以真并行,總時間只需要 10秒,而協程的方案仍然需要 20.1秒,
四 場景題
現在要開發一個點擊螢屏開炮發射子彈的功能,說下你的做法?
答:
首先把子彈進行抽象,把屬性和行為方法提煉出來,比如具有速度、威力、碰撞大小等屬性,具有飛行、碰撞和傷害等行為,
封裝子彈的抽象類,可以不繼承MonoBehaviour,
監聽螢屏點擊事件,觸發開炮邏輯,子彈通過物件池管理,復用子彈,防止因為頻繁創建銷毀帶來的性能問題,另外,子彈的坐標更新,可以統一由一個彈道控制器的Update遍歷每個子彈物件來計算,而不是每個子彈都掛一個MonoBehaviour去更新,因為MonoBehaviour的Update是通過反射被呼叫的,如果有1000顆子彈,就會呼叫1000次反射,這樣性能上比較差,
如果要做好幾種彈道的子彈,可以繼承子彈基類,拓展出多種子彈子類,子類中各自實作自己的UpdatePosition介面,彈道管理器通過Update遍歷每個子彈呼叫基類的UpdatePosition介面,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/241921.html
標籤:其他
下一篇:Vue實作隨機驗證碼功能
