How to make a Video Game in Unity 的課程筆記。
1. 基礎(chǔ)
- Scene 視圖,滾動鼠標(biāo)中鍵縮放,opt+左鍵以視圖中心為軸心旋轉(zhuǎn)。
- Hierarchy 視圖中的元素
- Inspector 放 components,使對象做不同的事
- Assets 資源,拖放到視圖中,會在 Hierarchy 中顯示
- 左上角 5 個 icon 對應(yīng) q, w, e, r, t 快捷鍵,可以移動畫幅,移動對象,旋轉(zhuǎn)對象,變形對象,縮放對象
- Game 視圖,按 shift+space 切換全屏與小窗口
- Command+P 運(yùn)行游戲
創(chuàng)建項(xiàng)目
- 創(chuàng)建 cube,Scene 中按 F 會聚焦到所選擇的對象。
- Inspector > Transform 中點(diǎn)右鍵 reset,會把該對象以原始大小重置到視圖中央。
- Game 視圖中以 Camera 的角度觀察。
- 設(shè)置地面:修改 cube 的 scale,移動到 camera 前一點(diǎn)的位置。改名為 Ground
- 創(chuàng)建 Player:再創(chuàng)建一個 cube,reset,拖到 ground 之上,改名 Player.
- 給 Player 上色:Assets 中新建 Material,名為 PlayerMat,Albede 改為紅色,拖到 Player 上。
- 使 Player 有物理特性:Add Component > Physics > Rigidbody。
- 是什么使對象有不同的特性呢?答案是 components。Transform 每個對象都有的屬性;Mesh Renderer 渲染對象到視圖;Box Collider 碰撞屬性。每個物理對象都需要 Box Collider 和 Rigidbody。Cube(Mesh Filter) 選擇不同的形狀。
- 如果改成球體,box collider 右鍵 remove component,然后 add component > Sphere Collider。
- 改變背景色:選擇 mainc camera > Inspector > Camera > Clear Flag > Solid Color, background 選擇淺灰色
- 保存 scene,在 unity 中,scene 就是 level。
2. 編程
選擇 Player,add component > script,輸入 PlayerMovement
可以拖放 script 到某個對象上,使它產(chǎn)生作用。
-
Unity 5.5.2 會自動產(chǎn)生這兩行,刪除掉:
using System.Collections; using System.Collections.Generic;
-
Start () 在開始執(zhí)行,Update () 每一幀執(zhí)行。我們要訪問 rigidbody
public Rigidbody rb;
然后在 inspector 面板中,把 Rigidbody 這個 component 拖到 rb 變量中,以便在代碼中引用。
-
加驅(qū)動力給 Player,為了避免計算快的計算機(jī)比慢的運(yùn)動速度不一致,將參數(shù)乘以 Time.deltaTime
rb.AddForce(0, 0, 2000 * Time.deltaTime)
放在 FixedUpdate() 中,當(dāng)對象和別的物體碰撞的時候會更順滑一些。
3. 移動
不讓 Player 滾動,只需要滑動,可以勾選 Constraints > Freeze Rotation > x,不過我們希望碰撞的時候可以滾動,所以不要這樣設(shè)置。
導(dǎo)致 Player 滾動的是摩擦力,Assets > 右鍵 > Create > Physic Material,命名為 Slippery,Inspector 中 Dynamic Friction 和 Static Friction 都設(shè)為 0,拖放到 Ground 上。
-
要在 inspector 中修改速度,創(chuàng)建一個速度變量:
public float forwardForce = 2000f;
修改
rb.AddForce (0, 0, forwardForce * Time.deltaTime);
-
判斷用戶輸入,左右移動 Player
public float sidewaysForce = 500f; if ( Input.GetKey("d") ) { rb.AddForce (sidewaysForce * Time.deltaTime, 0, 0); } if ( Input.GetKey("a") ) { rb.AddForce (-sidewaysForce * Time.deltaTime, 0, 0); }
4. 攝像頭
Camera 跟隨 Player: 可以把 Main Camera 拖到 Player 下,使它成為 Player 的子對象,不過當(dāng) Player 碰撞滾動的時候 Camera 也跟著滾,太詭異,所以我們將用代碼來實(shí)現(xiàn)。
-
給 Main Camera 添加 script —— FollowPlayer
using UnityEngine; public class FollowPlayer : MonoBehaviour { //只需要用到 transform,所以不需要引用到 GameObject public Transform player; //使得在 Inspector 中可以設(shè)置三個參數(shù) x = 0, y = 1, z = -5 //橫向x不變,縱向 y 向上一點(diǎn),沿著 z 軸跑的,放后面一點(diǎn) public Vector3 offset; // Update is called once per frame void Update () { //使 Camera 在 Player 的后上方 //不必 x, y, z 分別計算,兩個屬性都有這 3 個參數(shù),所以可以直接相加 transform.position = player.position + offset; } }
這樣當(dāng)碰撞的時候 Player 在滾動,Camera 仍然在它后方穩(wěn)定觀察。
5. 碰撞
-
為 Player 創(chuàng)建 PlayerCollision.cs
using UnityEngine; public class PlayerCollision : MonoBehaviour { //引用另外一個 script public PlayerMovement movement; //碰撞的時候會自動呼叫這個方法 void OnCollisionEnter (Collision collisonInfo) { if (collisonInfo.collider.tag == "Obstacle") { //發(fā)生碰撞時,使驅(qū)動 Player 滑動的 script 失效 movement.enabled = false } } }
在 inspector 中把 PlayerMovement 拖到 PlayerCollision 的接口中。
6. 游戲設(shè)置
把 hierachy 中的 Obstacle 拖到 Assets 面板中,這個對象就變成一個 prefab,這個預(yù)制件可以在游戲中重復(fù)使用,拖放多個 prefab 到 Scene 中,修改 prefab 會使所有 prefab 都發(fā)生改變,也可以單獨(dú)修改某個 Obstacle,修改某個對象后點(diǎn)擊 inspector 中的 apply 會使當(dāng)前修改應(yīng)用到所有 prefab 對象。
通常我們會讓 obstacle 向 player 移動,造成前進(jìn)的視覺,但是這個 demo 中我們讓 player 移動。
延長 Ground:Inspector 中設(shè)置 Transform.Position.z: 4980, Scale.z: 10000
Scene 右上角,點(diǎn)擊 y 軸,這樣視角就改成從頂部往下看,點(diǎn)擊中心的方塊,會取消掉透視效果,這樣我們可以在 2d 效果中設(shè)置游戲障礙物。
Inspector > Layers > Edit Layers, 添加 Environment Layer;選擇 Ground,Inspector > Layer > Environment;下拉 Layers,點(diǎn)擊 Environment 右側(cè)鎖頭鎖住。這樣我們再也不能選中視圖中的 Ground 了。這樣便于設(shè)置 ground 上的障礙物。
選擇障礙物,如果點(diǎn)擊綠色方框,那么只會沿著 z 和 x 軸移動。
設(shè)置吸附:菜單 Edit > Snap Settings...,設(shè)置 2, 2, 2, 按住 Command 再拖動的障礙物的時候,它會 2 個單位移動每一步。
復(fù)制多個障礙物,布滿 ground。
-
Player 響應(yīng)方向改變有點(diǎn)遲鈍,rb.AddForce 方法的幾個參數(shù)重,首先給 x 加力度,然后是 y, 然后是 z,最后是 Force Mode,F(xiàn)orceMode 默認(rèn)是 forced,它根據(jù)重量添加一個持續(xù)的壓力給 rigidbody,我們想要的是當(dāng)速率改變時,完全忽略這個物體的重量。所以添加 ForceMode.VelocityChange 參數(shù)。
if ( Input.GetKey("d") ) { rb.AddForce (sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange); } if ( Input.GetKey("a") ) { rb.AddForce (-sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange); }
看起來響應(yīng)比較及時了,為了讓運(yùn)動看起來更有逼真,設(shè)置 drag 屬性,drag 意味著空氣阻力,drag 越大,我們的 Player 越容易被空氣攔住停止,此外我們還要增加推力,以便保持運(yùn)動速度。drag:1, forwardForce: 4000
障礙物看起來好像突然冒出來一樣,我們要讓它們漸漸出現(xiàn)。菜單 window > lighting > 勾選 Fog > Fog Color 吸附背景色 > Density: 0.02
有時候 Player 會穿越障礙物,為了增加更精確的碰撞感知,Player 和 Obstacle 的 Rigidbody > Collision Detection 都設(shè)置為 continuous
Edit > Project Settings > Time > Fixed Timestep : 0.02,這樣 unity 會更頻繁地檢查物理狀況,減少錯過的碰撞。
提高 Player 的推力 8000
7. UI
Hierachy 面板,右鍵 > UI > Text
Scene 點(diǎn)擊頂部 2d,點(diǎn)擊 cavas,點(diǎn)擊 scene,敲F,聚焦到 UI 視圖
設(shè)置 text 位置,字體大小,外框大小,按 option 鍵從中間縮放,Horizontal Overflow 設(shè)為 overflow 意味著如果文字超出也能顯示。
選擇 canvas,設(shè)置 UI Scale Mode: Scale With Screen Size, Match Height
下載 roboto 字體(select后點(diǎn)小窗點(diǎn)download)。解壓。拖到 assets 中。選擇 text,選擇字體。
-
給 text 添加 script
using UnityEngine; using UnityEngine.UI; //設(shè)置 text 需要引入 UI public class Score : MonoBehaviour { //只需要位置信息,所以不引用 GameObject public Transform player; //引用 Label public Text scoreText; // Update is called once per frame void Update () { //轉(zhuǎn)換浮點(diǎn)數(shù)為 string 并且只保留整數(shù) scoreText.text = player.position.z.ToString("0"); } }
選擇 canvas,Inspector 勾選 Pixel Perfet,這樣字體會渲染得更銳利。
8. 游戲結(jié)束
創(chuàng)建 GameObject,reset transform。重命名為 GameManager。給它添加 script: GameManager,它將負(fù)責(zé)管理游戲狀態(tài)。比如更新游戲分?jǐn)?shù),切換菜單,切換游戲?qū)蛹壍鹊取N覀円龅氖鞘『笾刂糜螒颉?/p>
Player 碰撞的時候要呼叫 GameManager,如果直接引用它,有時我們需要讓 Player 死掉,這時 它所綁定的 GameManager 也就不存在了。因此我們不用引用變量的方法。
把 Player 拖到 Assets 面板,變成 Prefab。 Main Camera 和 text 所引用的 Player 都要重新設(shè)置。
-
當(dāng)需要的時候我們搜索這個 GameManager。GetComponent 和 FindObjectOfType 這兩個方法都類似。
using UnityEngine; using UnityEngine.SceneManagement; public class GameManager : MonoBehaviour { //避免重復(fù)呼叫 EndGame() bool gameHasEnded = false; public float restartDelay = 1f; public void EndGame(){ if(gameHasEnded == false){ gameHasEnded = true; //延遲執(zhí)行,第一個參數(shù)是呼叫的方法名,第二個參數(shù)是延遲的秒數(shù) Invoke ("Restart", restartDelay); } } void Restart(){ //重載當(dāng)前 scene SceneManager.LoadScene (SceneManager.GetActiveScene ().name); } }
-
修改 PlayerCollison.cs
//movement.enabled = false; GetComponent<PlayerMovement>().enabled = false; //找到 GameManager FindObjectOfType<GameManager> ().EndGame();
-
當(dāng) Player 掉下 Gound 時呼叫 EndGame。修改 PlayerMovement.cs
if (rb.position.y < -1f){ FindObjectOfType<GameManager> ().EndGame (); }
修正 lighting:Window > Lighting,去掉底部 auto 選項(xiàng)。auto 表示 unity 自動檢查光線,當(dāng)重載 scene 的時候,unity 時間去計算光線,它會使畫面非常亮,所以要去掉。點(diǎn) build。
如果多于一個 scene,我們還要 File > Build settings,把 Scene 拖進(jìn)去。build and run