從零開始獨(dú)立游戲開發(fā)學(xué)習(xí)筆記(五)--Unity學(xué)習(xí)筆記(二)--Unity基礎(chǔ)(二)

邊聽嵐少直播,邊繼續(xù) Unity 的學(xué)習(xí)。
1. Physic Material
上一次做 cube 的時候。cube 接觸地面后會向前滾。這是因?yàn)橛心Σ亮?。為了讓物體平滑地滾動,我們可以去掉摩擦力。
在 project panel 里新建一個 Pyysic Material。用法和 Material 一致。直接拖到 Object 上即可。2. 然后可以在 Box Collider 里看到這個 asset 文件。雙擊該文件或者 project panel 里的文件可以打開關(guān)于這個 physic material 的 Inspector。可以改變其屬性。
將兩個摩擦力參數(shù)都改成 0,運(yùn)行游戲,發(fā)現(xiàn) cube 不會再滾動了。
2. 變量
為什么要專門提到變量。因?yàn)?Unity 可以讓你在 Inspector 里看到并改動 C# Script 里變量的值,只需要聲明變量為 public。然后變量就可以出現(xiàn)在 Script 組件的屬性里了。
其實(shí)之前聲明的 RigidBody 類型的 rb,也是一個變量。然后后續(xù)連接的時候我們把 cube 的 RigidBody 賦值給這個變量。
順帶一提,Unity 里的數(shù)值變量/屬性,可以把鼠標(biāo)移到這行屬性左邊的屬性名上,可以看到鼠標(biāo)箭頭變了。表示可以左右拖拽改變數(shù)值。因?yàn)闆]有明顯的提示所以提一下。
3. 按鍵
在 Update 里可以監(jiān)聽按鍵事件。使用?Input.GetKey("w")
?即可。 此外 input 事件在 Update 里監(jiān)聽更好,而不是 FixedUpdate。比如說在 Update 里監(jiān)聽按鍵。然后賦值給一個布爾變量,再在 FixedUpdate 里根據(jù)這個變量來判斷。
4. FixedUpdate 和 Update 的區(qū)別
Update 的調(diào)用是每幀調(diào)用一次。但是每一幀的處理時間很可能不一樣。
而 FixedUpdate 是按照固定時間間隔調(diào)用的,而且 FixedUpdate 調(diào)用后會立刻進(jìn)行物理計算。因此任何對 RIgidBody 的物理運(yùn)算都應(yīng)該在 FixedUpdate 里進(jìn)行,因?yàn)槟惝?dāng)然希望你的代碼運(yùn)行后能夠立刻改變物理狀態(tài)。
所以如果用 FixedUpdate 就可以不需要用 Time.deltaTime 來 normalize 了。 (不過有自己的 FixedDeltaTime,是一個固定值。當(dāng)然你也可以在游戲運(yùn)行時改變它以調(diào)整 FixedUpdate 的運(yùn)行速率)
即使在 Update 里用 Time.deltaTime 也不如用 FixedUpdate。因?yàn)?Time.deltaTime 畢竟是上一幀花費(fèi)的時間,而不是這一幀。也許一秒內(nèi)移動的距離是固定住了,但是物理運(yùn)行速度不穩(wěn)定。
但是像是按鍵輸入這種行為,則一定要放在 update 里。這是為什么呢?
舉個例子,你按了一下跳躍鍵,因?yàn)槭翘S鍵,所以你只按一次。然后你按鍵的這一剎那,剛好沒趕上 FixedUpdate 執(zhí)行,于是你的按鍵沒有被監(jiān)聽到,因此你跳不起來,這甚至連延遲都不是,直接就是沒監(jiān)聽到。
但是你放在 update 里,則是一定會被監(jiān)聽到的。
5. 相機(jī)跟隨
如果想讓相機(jī)跟隨物體,可以直接把相機(jī)拖到物體上,成為其 child。但是這樣一來讓物體旋轉(zhuǎn)的時候相機(jī)也跟著旋轉(zhuǎn)了,這樣不好。所以我們用腳本來實(shí)現(xiàn):


我們讓 camera 的 position 等于 cube 的 position 加上一個偏移值。
Vector3 儲存 3 個 float 值。
6. 碰撞
創(chuàng)建一個障礙物,當(dāng)碰撞的時候,讓 player 無法行動,代碼如下:

其中 PlayerMovement 其實(shí)就是之前我們創(chuàng)建的 Sript 的名字(因?yàn)闀凑?Script 名字自動創(chuàng)建一個類,然后創(chuàng)建的就是那個類的類型變量),然后在 Unity 里將其鏈接。最后在 c# 里將其 enabled 關(guān)掉即可停止 player 的行動。
OnCollisionEnter 會在物體碰撞發(fā)生的時候調(diào)用??山邮芤粋€參數(shù),就是這個碰撞事件。這個碰撞事件里的 collider 就是撞到的物體。
tag 可以在 Unity 里添加。Inspector 的右上角,有一些內(nèi)置的 tag,但我們這里自己添加一個新的 Obstacle tag。

6. Prefabs
當(dāng)需要創(chuàng)建一群相同的 Objects 的時候,一個一個復(fù)制過去雖然也可以,但是當(dāng)你需要對這些 Objects 進(jìn)行統(tǒng)一修改的時候那就麻煩了。于是出現(xiàn)了 Prefab 這么一個概念。
創(chuàng)建方式是從 Hierachy panel 里把想要變成 Prefab 的 Object 直接拖到下方 Project Panel 里。就變成了一個 Prefab,然后可以直接把這個 Prefab 拖到中央視圖。就可以創(chuàng)建一個個相同的 Objects 了。
此時如果在 Prefab 的 Inspector 里改動,所有改動都會影響到所有通過這個 Prefab 創(chuàng)建的 Objects。
也可以改動單獨(dú)的 Object,不會對其他的有影響。如果想把這個改動映射到其它 Objects 上,可以選擇 Object 的 Inspector 里上方的 Override 屬性里選擇把哪些改動映射到其它 Objects 上。
7. 透視
Scene view 里右上角的那個顯示 x,y,z 軸的東西,可以點(diǎn)擊 x,y,z 軸來跳轉(zhuǎn)到那個軸的視角上。
而點(diǎn)擊中央則可以切換是否透視。
這是 isometric 的(我也不知道怎么翻,等比例?):
可以看到地面是平行的,前后所有線都是平行,不符合透視規(guī)則

2. 這是 perspective 的(透視:) - 可以看出透視的規(guī)律(圖中綠線)

除了畫面右上角,選擇物體的時候也有 x,y,z 軸顯示(Which 你已經(jīng)看到過也用過很多次了),拖動 x,y,z 其中任意兩軸之間的那個小方塊,可以同時移動這兩個軸。按住中間的那個方塊則是所有軸一起改動。
8. Layer
當(dāng)你的項(xiàng)目比較復(fù)雜,有些時候場景東西實(shí)在是太多。當(dāng)你想鼠標(biāo)左鍵拉框選取其中幾個 Objects 的時候,總是會把一些諸如地面,草地 等你不想選中的東西選到。而如果在 hierachy panel 里選又太麻煩了,因?yàn)檫@些東西不一定在一起。你也不一定能在 hierachy 里區(qū)分出它們。
那么這個時候就可以用 layer 來將你的 object 分層。(或者理解為 group 也可以)我們把這些環(huán)境的東西專門放到一個 layer 叫做 environment,然后讓這個 layer 里的東西無法通過鼠標(biāo)被選中就可以了。
我們點(diǎn)擊地面,然后在 Inspector 上方的 layer 里,可以看到其實(shí) unity 已經(jīng)有一些預(yù)制 layer 名字了,并且可以看到我們之前生成的 Object 都已經(jīng)被放在一個叫做 Default 的 layer 里了。
這里我們選擇 Add Layer 創(chuàng)建一個新的層,取名叫做 Environment。(注意雖然你是在選擇地面后里加的 Add Layer,但是 Unity 并不會給你自動綁定上去,所以還要你點(diǎn)到地面,再在 layer 里選擇 Environment 才行)
然后我們點(diǎn)擊屏幕右上角的 Layer(調(diào)整布局左邊的那個),然后將 Environment 設(shè)置成無法選中(截圖標(biāo)紅的那個):

之后中央視圖里鼠標(biāo)左鍵拉框的或者直接點(diǎn)到地面的時候,就不會選中地面了。只能在 hierachy 里選擇。
9. snap
中央視圖上方一排工具里,可以選擇顯示網(wǎng)格。然后當(dāng)按住 control 鍵,再移動物體的時候,就會附著在網(wǎng)格上。然后網(wǎng)格工具右邊的一個按鈕可以設(shè)置 snap 的大小,比如說設(shè)置為 1 的時候,按住 control 后,物體就只能 1 格 1 格地移動。也可以設(shè)置為 1/3(可以直接打1/3,Unity 會自動計算),這樣物體就只能 1/3 格地移動。對齊的時候很有效。
10. fog
遠(yuǎn)方的東西如果都能看到,會很影響視野。和近處的東西混在一起。這個時候可以加 fog。在上方工具欄里選擇 Window->Rendering->Lighting。可以把它拖到 Inspector 右邊粘附成一個 tab 頁。其下的一個叫做 Environment 的 sub-tab 里,勾選 Fog。可以調(diào)整霧的顏色(比如說吸色成 skybox 的顏色),霧的濃度等等
11. 高速物體的碰撞檢測失效
如果物體速度過快,碰撞檢測是有可能失效的。
解決方案是在 Obstacle Prefab 的 RigidBody 組件里將 Collision Detection 改成 continuous。cube 的改成 continuous dynamic。
然后在 Project Setting->Time 里減少 Fixed Timestep。其實(shí)這個就是?Time.fixedDeltaTime
,減少了之后,F(xiàn)ixedUpdate 運(yùn)行頻率就會更快,也就是說物理更新也會更快。
12. UI
我們想創(chuàng)建一個在屏幕中央顯示已獲得分?jǐn)?shù)的 UI。
在 hierarchy 中創(chuàng)建一個 UI->Text object。hierarchy 會新建一個 canvas->text Object。canvas 就是用來放置你的 UI Object 的。
toggle 中央視圖為 2d,然后按 f 自動 focus 到剛創(chuàng)建的 text 上。
在 Inspector 里把 vertical overflow 設(shè)置為 overflow,不然高度太高的話,文本直接全部消失。
調(diào)整文本大?。╢ont size)。選文本對齊方式(Alignment)(中央居中,垂直居中等)。
下一個字體,直接拖到 Asset 里。然后換成這個字體。text 里可以更改文本字符串。
還有個問題,此時你把屏幕放大縮小,字體大小不變。但我們想根據(jù) window size 而變化。那么我們需要進(jìn)入 canvas 的 Inspector,將 Canvas Scaler->UI Scale Mode 改成 Scale With Screen Size,然后將 Canvas Scaler->Match 拖到最右邊(和 Height 匹配)。
這樣我們 UI 的樣式就調(diào)整好了。
注:Asset 文件夾太亂了,可以在里面建立 folder 整理一下。Asset 文件夾本身也是對應(yīng)你項(xiàng)目文件夾里的 Asset 文件夾的。你導(dǎo)入文件其實(shí)就是往那個文件夾里復(fù)制文件罷了。
接下來我們想實(shí)時更新這個 text 的文本(有點(diǎn)語病哈,但是前者 text 指的是 text 這個 UI,后者“文本”指的是這個 text 的文本內(nèi)容),那么就需要 Script 了:
創(chuàng)建一個 Script。
在 Script 里引用 player 的 transform 信息。之前講過了就不說了。
代碼如下:

13. Game Over
我們想讓游戲結(jié)束后可以重來,UI 的展示等等。我們可以用一個 Game Manager 來做這件事。
我們在 Hierarchy 里創(chuàng)建一個空物體,添加腳本,命名為 GameManager。可以看到 Unity 會自動識別這個名字,然后 asset 處顯示的 icon 就變成了一個齒輪。我們在里面重置游戲,如下代碼:

然后我們在之前寫的 PlayerCollision 里運(yùn)行這段腳本就好了。
這里有一個問題,怎么引用這個腳本,像之前那樣,因?yàn)槟_本本身會新建一個 class,我們建一個變量儲存?
可惜這樣不行。因?yàn)楫?dāng)我們重置游戲后,player 被銷毀再重建,就會忘記銷毀前引用的變量(當(dāng)然,引用自己的或者非引用類型的不會),同理其他 Object 上引用這個 object 的變量也會消失。
因此我們要實(shí)時地去尋找,代碼如下:

不只是碰撞發(fā)生的時候需要重置游戲,當(dāng) cube 掉出去的時候也需要:

當(dāng)然以上不需要用?
GetComponent
,直接用?rb.position.y
?就可以,只是演示一下。以上還是有 bug,當(dāng)重置游戲的時候,會發(fā)現(xiàn)光照都沒有了。因?yàn)槲覀兏淖兞斯庹赵O(shè)置,卻沒有生成。然后現(xiàn)在 auto 是關(guān)掉的。所以我們?nèi)ブ按蜷_過的 Lighting 面板,在 Workflow Setting 里 Generate Lighting 一下即可。
然后我們需要加一個延遲,當(dāng)游戲結(jié)束后等幾秒再重置,代碼如下:

14. 游戲勝利
14.1 添加 Trigger
創(chuàng)建一個 cube,拉長放在你想結(jié)束游戲的位置,當(dāng) player 碰到它時勝利。不過我們要把它的 Mesh Renderer 關(guān)掉,這樣就看不見它了。
但是問題來了,我們又選不中它了,所以要加一個 icon 以便選中。在 Inspector 里面左上方可以選擇 icon。不過可能顯示的比較小或者不透明度太低。我們在 scene view 上方的 gizmos 列表里最上方找到 3d icons,把拖拽條往右邊拖就可以讓 icon 顯示地更明顯。
把 isTrigger 打開,這樣就會被物理引擎忽視,player 碰到它會無視掉直接穿過。
我們新建一個 Script,里面寫一個 OnTriggerEnter 函數(shù),當(dāng)有 Object 碰到這個 trigger 時這個函數(shù)就會被調(diào)用。(所以不要讓 trigger 和其它 Object 重合,以免游戲剛開始就觸發(fā)了)(當(dāng)然也可以在里面判斷一下碰撞的是哪個 Object 就是了)
14.2 添加 panel
我們希望勝利的時候,畫面變成通關(guān)提示。
在 hierarchy 上右鍵,新建一個 UI->panel,這樣就會在 canvas 下新出現(xiàn)一個 panel。
進(jìn)入 panel inspector,將 source image 設(shè)置成 None,意思是我們用純色當(dāng)背景。設(shè)置一下 color,如果你發(fā)現(xiàn)顏色半透明,可能是 color 部分不透明度較低(準(zhǔn)確來說是 Alpha 值)。
在 panel 上而不是空白處右鍵新建 text,這樣 text 就會在 panel 下作為 child,而不是平級。
調(diào)整一下想要的樣式。
Inspector 最左上方,將勾選去掉,可以看到屏幕中已經(jīng)不顯示這個 panel 了,我們要在 Script 中啟用它。
14.3 勝利腳本
雖然可以在 OnTriggerEnter 里操作,但是游戲勝利這種應(yīng)該在 GameManager 里操作。所以我們在 OnTriggerEnter 所在腳本中引入 GameManager。這里我們不需要用?
FindObjectOfType
,因?yàn)槲覀儾粫N毀再重新生成 trigger。直接用變量引用即可。然后在 GameManager 里 enable 我們剛建好的 panel。
// GameObject 引用整個 Objectpublic GameObject WinningPanel;public void Win(){ ? ?// 啟用
? ?WinningPanel.SetActive(true);
}
14.4 動畫
直接顯示 pane了沒什么意思,加動畫比較有趣,所以我們開始加動畫。
首先把動畫窗口打開,上方工具欄里 window->Animation->Animation。(拖到下方附著比較好)
創(chuàng)建一個 animation,會打開資源管理器,選擇放置的位置。我們新建一個文件夾叫做 Animation,然后在里面創(chuàng)建即可。
然后打開 project panel 可以看到里面有兩個文件。一個是我們創(chuàng)建的 Animation,關(guān)掉 loop 因?yàn)槲覀儾幌胍5诙€是 Animation Controller,控制 Animation 的各個狀態(tài)等。
回到 Animation 視窗(選中 UI panel 的情況下),可以看到 timeline。點(diǎn)擊左邊紅色的 recording 按鈕,timeline 變成紅色,就可以記錄所有的改動了。Aniamtion 的創(chuàng)造和 AE(視頻后期軟件那個 AE) 一樣,靠關(guān)鍵幀。進(jìn)入 recording 狀態(tài)后,所有的改動都會變成 keyframe。然后就該怎么做怎么做吧。