一、概要
在 Unity入門教程(上) 中我們創建了一個游戲項目,並且創建了玩家角色和小球這些游戲對象,還通過添加游戲腳本實現了小方塊的彈跳。雖然功能比較簡單,但是完整地表現了使用Unity開發游戲的大體流程。
為了讓這個游戲變得更加有趣,下面我們要進一步完善玩家角色和小球的動作。
二、讓小球飛起來(物理運動和速度)
目前小球是靜止在空中的,下面我們來嘗試讓它朝玩家角色飛去。
為了令小球能夠模擬物理運動,需要添加Rigidbody組件。同時還需要創建一個Ball的腳本。此操作在Unity入門教程(上)中的步驟十和步驟十一。
添加了Ball腳本以后,我們要對Start方法作如下修改
void Start () { this.GetComponent<Rigidbody>().velocity = new Vector3(-8.0f, 8.0f, 0.0f); //設置向左上方的速度 }
游戲開始后,小球將向畫面左側飛去
三、創建大量小球(預設游戲對象)
為了能夠隨時創建出小球對象,首先需要對小球對象進行預設。
1,請將層級視圖中的Ball項文本拖拽到項目視圖中
項目視圖中將出現Ball項。同時,層級視圖中的Ball項文本將會變為藍色。
2,將項目視圖中的Ball預設拖拽到場景視圖中
可以看到場景中會多出一個小球對象。
預設了游戲對象后,我們就能夠非常容易地創建出多個同樣的物體。
3,將Player和Floor游戲對象也做成預設
四、整理項目視圖
1,用文件夾將這些項目歸類整理
在項目視圖左上角的菜單中點擊Create→Folder后,項目視圖中將生成一個文件夾,將名字改為Prefabs。
2,將預設Ball Prefab拖拽到Prefabs文件夾下
點擊Prefabs文件夾,可以看到剛才移動的Ball預設。接着把Player預設和Floor預設也移動到Prefabs文件夾下。
3,采用同樣的方式創建Scenes、Scripts、Materials文件夾,並把各項目放到相應的文件夾下
注意在創建前務必先點擊項目視圖左側的Assets圖標以確保當前文件夾回到Assets。
五、發射小球(通過腳本創建游戲對象)
1,在窗口頂部菜單中依次點擊GameObject→Create Empty
由於該游戲對象被用作發射台,因此命名為Launcher
2,對游戲對象Launcher進行預設
3,創建Launcher腳本
4,將Launcher腳本添加到Launcher預設中去(另外一種方法)
(1)在項目視圖中切換到Prefabs文件夾,點擊選中Launcher預設。此時檢視面板上將顯示Launcher的相關信息,然后點擊最下方的Add Component按鈕
(2)在標題為Component的下拉菜單中點擊最下方的Script項。點擊后菜單將向左移動,顯示出所有創建好的腳本。找到Launcher腳本並點擊。
小結:現在我們已經知道在檢視面板中也可以添加組件,除此之外,還可以使用窗口頂部菜單或者直接拖拽。
5,編輯Launcher腳本
除了Update方法有變動之外,還增加了ballPrefab變量。
Instantiate是通過預設生成游戲對象實例的方法。不過腳本中並沒有對ballPrefab變量進行初始化的代碼,所以在游戲運行前必須先在檢視面板中對ballPrefab變量賦予預設對象值。
public class Launcher : MonoBehaviour { public GameObject ballPrefab; //小球預設 // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Input.GetMouseButtonDown(1)) //點擊鼠標右鍵后觸發 { Instantiate(this.ballPrefab); //創建ballPrefab的實例 } } }
從項目視圖中選擇Launcher預設。可以看到在檢視面板中的Launcher(Script)標簽下顯示有Ball Prefab項。腳本代碼中聲明的所有public成員變量都將在這里列出。
往類中新添加的變量默認表示為None(GameObject),意味着該變量還未被賦值。請將項目視圖中的Ball預設拖拽到這里(鼠標左鍵按着不要松手)。
6,運行游戲
每次單擊鼠標右鍵時,都會射出一個小球。
這里,為了和預設對象分開,我們把腳本中通過Instantiate方法生成的游戲對象稱為實例,把產生實例的過程稱為實例化。
六、刪除畫面外的小球(通過腳本刪除游戲對象)
我們的游戲現在出現了一個Bug:發射出去的小球永遠不會消失。
游戲運行時由腳本動態生成的游戲對象也會被顯示在層級地圖中。每點擊一次鼠標,層級視圖中都會增加一個Ball(Clone)游戲對象。因此即使小球已經跑出游戲畫面之外,這些游戲對象也並未消失。
跑出畫面之外的小球不會再回到畫面中,所以完全可以刪除。
在腳本Ball.cs中添加OnBecameInvisible方法,該方法可以被添加到Ball類定義范圍內的任意位置。
public class Ball : MonoBehaviour { //添加:游戲對象跑出畫面外時被調用的方法 void OnBecameInvisible() { Destroy(this.gameObject); //刪除游戲對象 } // Use this for initialization void Start () { this.GetComponent<Rigidbody>().velocity = new Vector3(-8.0f, 8.0f, 0.0f); //設置向左上方的速度 } // Update is called once per frame void Update () { } }
OnBecameInvisible方法是在游戲對象移動到畫面之外不再被繪制時被調用的方法。
Destroy(this.gameObject)則是刪除游戲對象的方法。
注意:如果把參數設置成this的話,刪除的就不是游戲對象,而是Ball腳本組件。
七、防止玩家角色在空中起跳(發生碰撞時的處理)
為了防止玩家角色在空中再次起跳,我們來添加下列處理
- 添加着陸標記
- 着陸標記值為false時不允許起跳
- 將起跳瞬間的着陸標記設為false
- 將着陸瞬間的着陸標記設為true
修改Player腳本,代碼如下:
public class Player : MonoBehaviour { protected float jump_speed = 8.0f; //設置起跳時的速度 public bool is_landing = false; //着陸標記 // Use this for initialization void Start () { this.is_landing = false; } // Update is called once per frame void Update () { if(this.is_landing){ //着陸后觸發 if(Input.GetMouseButtonDown(0)){ this.is_landing = false; //將着陸標記設置為false(未着陸 = 在空中) this.GetComponent<Rigidbody>().velocity = Vector3.up * this.jump_speed; } } } //添加:和其他游戲對象發生碰撞時調用的方法 void OnCollisionEnter(Collision collision) { this.is_landing = true; //將着陸標記設置為true(着陸 = 在地面上) } }
當一個游戲對象同其他對象發生碰撞時,OnCollisionEnter方法將被調用。
這是為了檢查玩家角色是否着陸而添加的。在該方法中把着陸標記的值設為true。這樣玩家角色就不能在空中再次起跳了。
八、禁止玩家角色旋轉(抑制旋轉)
在某種程度上完成了玩家角色和小球的腳本編程后,讓我們來調整各相關參數,以使角色在起跳后能和小球發生碰撞。
這里我們采用下列值:
- 玩家角色的位置:(-2.0,1.0,0.0)
- 玩家角色的起跳速度(Player.cs腳本中jump_speed的值):8.0
- 小球的位置:(5.0,2.0,0.0)
- 小球的初始速度(Ball.cs腳本中使用Start方法設定的值):(-7.0,6.0,0.0)
1,選擇項目視圖中的Player並打開檢視面板中的Rigidbody標簽下的Constraints項
2,點擊左邊的三角形圖標,下面會進一步顯示Freeze Position和Freeze Rotation
其中Freeze Position對於將游戲對象的位置坐標固定在某些方向上,Freeze Rotation則用於固定其角度。
由於我們希望玩家角色只上下跳躍而不做左右和前后的移動,因此:
3,把Freeze Position的“X”“Z”前面的復選框選中。Freeze Rotation方面則把“X”“Y”“Z”全部選中
九、讓玩家角色不被彈開(設置重量)
選擇項目視圖中的Ball預設,打開Rigidbody標簽,將Mass項的值由1改為0.01。
Mass項用於設定游戲對象的重量。兩個游戲對象發生碰撞時,Mass值較大的物體將保持原速度繼續運動,相反Mass值較小的物體則容易因受到沖擊而改變移動的方向。
十、讓小球強烈反彈(設置物理材質)
1,創建物理材質
從項目視圖的Create菜單中選擇Physic Material,創建一個新材質並將其名稱改為Ball Physic Material
相對於用來指定顏色等可以看見的屬性材質,物理材質則是用於設定彈性系數和摩擦系數等與物理運動相關的屬性。
2,修改屬性值
在項目視圖中選擇Ball Physic Material后,在檢視面板中選擇Bounciness,將其值由0改為1。這個值越大,游戲對象越容易被“彈開”。
3,將新創建的材質拖拽到Ball預設下的Material
從項目視圖中選擇Ball預設,接着把Ball Physic Material拖拽到檢視面板中Sphere Collider標簽下的Material
或者可以點擊Ball Physic Material右側的圓形圖標。這時Select Physic Material窗口將被打開,在這個“物理材質選擇窗口”中也可以進行選擇設定
十一、消除“漂浮感”(調整重力大小)
1,在窗口頂部菜單中依次點擊Edit→Project Settings→Physics
檢視面板中將切換顯示PhysicsManager
2,將Gravity項的“Y”值稍微提高一些,在此設為-20
3,調整參數
通過增強重力可以減弱物體在運動時的“漂浮感”,不過跳躍的高度和小球的軌道也顯得比原來低了。這種情況下,我們可以考慮調整為下列數值:
- 玩家角色的起跳速度(Player.cs腳本中的jump_speed的值):12.0
- 小球的初始速度(Ball.cs腳本中使用Start方法設定的值):(-10.0,9.0,0.0)
十二、調整攝像機的位置
1,選擇攝像機后,場景視圖右下角將出現一個小窗口。這是從攝像機看到的畫面。如果無法看到這個窗口,請在檢視面板中展開Camera標簽
2,為了能夠俯視地面,需要使攝像機在往上偏移的同時繞X軸旋轉
調整角度時需把移動工具切換為旋轉工具。
用移動工具調整攝像機的位置
用旋轉工具調整攝像機的角度
3,在檢視面板中輸入數值(可根據自己喜好進行設置)
4,對比效果
調整攝像機前:
調整攝像機后:
十三、修復空中起跳的bug(區分碰撞對象)
1,bug的發現
試玩游戲后,我們注意到玩家角色和小球碰撞后還可以再次起跳。這可能是因為防止空中跳躍的代碼存在bug。
2,bug的證明
(1)游戲啟動后,在層級視圖中選擇Player。可以在檢視面板中的Player(Script)標簽下看到Is_landing項。這就是在Player腳本中定義過的is_landing變量
(2)游戲剛開始時畫面上還沒有小球。隨着玩家角色起跳,可以看到Is_landing復選框由取消變為了選中狀態
跳躍過程中Is_landing為取消狀態(值為false)
着陸后Is_landing為選中狀態(值為true)
(3)修改Player.Update方法
void Update () { if(this.is_landing){ //着陸后觸發 if(Input.GetMouseButtonDown(0)){ this.is_landing = false; //將着陸標記設置為false(未着陸 = 在空中) this.GetComponent<Rigidbody>().velocity = Vector3.up * this.jump_speed; Debug.Break(); } } }
修改后僅添加了Debug.Break方法的調用。在玩家角色起跳時的瞬間暫停游戲的運行。
按下播放控制工具條最右邊的按鈕,在逐幀模式下可以看到玩家角色在一直上升。在玩家角色和小球碰撞的瞬間,Is_landing的值變成了true。(此處無法截圖,見諒)
搞清楚了bug的原因,接下來就考慮解決bug的對策。
3,bug的解決
(1)首先需要區分開碰撞對象是地面還是小球
此處我們可以利用標簽。需要對游戲對象的種類進行大致區分時,可以使用標簽來分組。
添加標簽到項目中,在項目視圖中選擇Floor預設→點擊Untagged→點擊Add Tag→點擊Tags左側的三角形→點擊“+”→輸入Floor→再次在項目視圖中選擇Floor預設→點擊Untagged→點擊Floor
(2),修改腳本
修改Player.OnCollisionEnter方法。在這里提醒下:記得刪除了之前在Player.Update方法中添加的Debug.Break()。
void OnCollisionEnter(Collision collision) { if (collision.gameObject.tag == "Floor") { this.is_landing = true; //將着陸標記設置為true(着陸 = 在地面上) } }
使用了標簽后就可以區分碰撞對象了。這樣一來就只有在和地面碰撞時,也就是着陸時Is_landing的值才會變為true。
十四、小結
本次有關Unity入門的學習就暫時先告一段落。通過做一個小游戲項目的流程,讓我切身體會到使用Unity開發游戲的大致流程,還有遇到Bug時的分析思路。
當然如果想通過一個小游戲的制作就學會Unity的全部技能是不可能的,后期在游戲開發的過程中,遇到了問題再去查找相應的答案,見招拆招,才是最有效的。