本節書摘來異步社群《unity 4 3d開發實戰詳解》一書中的第6章,第6.5節,作者: 吳亞峰 , 杜化美 , 張月霞 , 索依娜 責編: 張濤,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。
unity 4 3d開發實戰詳解
在前面的内容中,講解了unity開發平台下實體引擎的相關内容,正是這一完善的實體引擎,使得模拟現實變得極其簡單。在本小節中,将通過一個交通工具的小案例來模拟現實生活中汽車的各種運動。下面将對交通工具案例的開發步驟進行介紹。
1.案例的構思
在開發案例之前,這裡先介紹一下本案例的設計思路。
(1)首先要明确案例要達到的目的。本案例是為了示範使用unity實體引擎模拟現實生活中交通工具的運動特性。
(2)接着要明确案例場景的設計。在本案例中,使用了地形引擎系統,在地形上有一些任意位置形狀的小山丘。同時,場景中有一輛汽車模型,在汽車正前方擺放有兩個油桶和3個木箱。這些場景的設計是為了觀察汽車在地形上的運動以及跟油桶和木箱的碰撞情況。
(3)然後是對汽車模型的開發。導入的汽車模型是不能夠運動的,必須給其添加必要的元件。在本案例中,汽車模型整體被添加了剛體和相應的腳本元件,車輪也被添加了車輪碰撞器元件和相應的腳本元件。
(4)最後就是把pc端的程式移植到android裝置上。本案例中采用unity引擎自帶的single joystick預制件來實作android裝置上的操縱杆控制汽車。
2.案例場景的設計
在明确了案例的構思後,就可以按照構思的步驟來進行開發了。主要的開發步驟如下。
(1)首先建立一個項目,選擇file→new project選項建立一個新項目,并命名為“vehicledemo”。然後,選擇file→save scene(或者按下快捷鍵ctrl+s)儲存場景為car。最後,選擇gameobject→create other→directional light來建立一個光源,如圖6-118所示。
(2)建立地形。選擇terrain→create terrain建立一個新的地形,然後使用地形編輯工具對地形進行适當的編輯,最終的地形效果如圖6-119所示。
說明 因為地形引擎系統不是本小節的重點,該内容将在後續的章節裡具體介紹,是以,本小節隻是簡單地說明,如果讀者目前還不習慣使用地形引擎系統,可以建立一個平面來替代,相信對認真學習過前面章節内容的讀者來說,建立一個平面應該不是什麼難題。

(3)導入資源。将CD光牒中本章節對應檔案夾下的“資源包”檔案夾中的“model.unitypackage”檔案複制到第(1)步中建立項目時生成的“vehicledemo”檔案夾下。然後,選擇assets→import package→custom package導入“model.unitypackage”中的所有檔案,如圖6-120所示。
(4)在場景中建立汽車和油桶模型。将“assetsmodel”檔案夾下的“car_model”拖曳到scene視圖中,如果汽車模型沒有顯示貼圖,則可以将“assetsmodelmaterials”檔案夾下的“car_material”材質分别拖曳到汽車模型的車身和4個車輪上。然後用同樣的方法,在場景中建立兩個油桶模型,分别命名為“oiltank1”和“oiltank2”,如圖6-121所示。
(5)在場景中建立3個木箱。選擇gameobject→create other→cube分别建立3個立方體,且分别命名為“box1”、“box2”、“box3”。然後将“assetstexture”檔案夾下的“woodboxtex”紋理貼圖分别拖曳到新建立的3個立方體上,調整場景中各個模型的transform參數,如圖6-122所示。
3.案例主體部分設計
前面剛介紹完場景的搭建,接下來将介紹案例主體部分的設計。這部分主要是為了增加案例運作過程中的真實性和互動性,這就需要給場景中的各個遊戲對象加上相應的元件,具體步驟如下。
(1)為油桶和木箱添加實體屬性。選擇component→physics→rigidbody為油桶和木箱添加剛體,接着選擇component→physics→box collider為油桶添加“box collider”元件(木箱已有該元件,不需重複添加)。同時,為木箱添加“wood”材質,為油桶添加“metal”材質,如圖6-123所示。
(2)為汽車的車輪添加“wheel collider”元件。選擇gameobject→create empty建立一個空對象,重命名為“flcollider”,然後選擇component→physics→wheel collider給“flcollider”添加碰撞體,并将“flcollider”的位置和汽車左前輪位置對齊。按照此操作步驟,依次建立“frcollider”、“blrcollider”、“brcollider”,并組織好目錄結構,如圖6-124所示。
(3)為汽車的車身添加“box collider”元件。選擇gameobject→create empty建立一個空對象,重命名為“bottomcollider”,然後選擇component→physics→box collider給“bottomcollider”添加碰撞體,并使碰撞體包裹住車身下半部分。按照此操作步驟,建立“topcollider”并使其包裹住車身上半部分,并組織好目錄結構,如圖6-125所示。
(4)為汽車的車輪編寫腳本。該腳本的作用是控制車輪的位置和旋轉參數,使車輪在車的移動過程中跟着轉動,這樣車輪的運動看起來更逼真。具體代碼如下所示。
1 var frontleftwheel : wheelcollider;//定義左前輪碰撞器
2 var frontrightwheel : wheelcollider;//定義右前輪碰撞器
3 var gearratio : float[];//定義齒輪比數組,程式會根據齒輪數比來決定應該給車輪多大的扭矩
4 var currentgear : int = 0;//定義目前齒輪索引
5 var enginetorque : float = 600.0;//定義發動機轉矩
6 var maxenginerpm : float = 3000.0;//定義發動機最大轉速
7 var minenginerpm : float = 1000.0;//定義發動機最小轉速
8 private var enginerpm : float = 0.0;//定義發動機轉速
9 var movejoystick : joystick;//定義操縱杆
10 function start () {
11 //給汽車質心的y方向的位置指派,确定汽車質心的位置是為了保證汽車在運動過程中不容易翻車
12 rigidbody.centerofmass.y = -1.5;
13 }
14 function update () {
15 rigidbody.drag=0.15 +rigidbody.velocity.magnitude/25;//設定汽車運作阻力,限制汽車速度
16 //計算汽車轉速
17 enginerpm = (frontleftwheel.rpm + frontrightwheel.rpm)/2 * gearratio[currentgear];
18 shiftgears();//調用方法
19 if(application.platform == runtimeplatform.android) {//如果程式運作在android平台上
20 var touchkey_x : float = movejoystick.position.x; //擷取操作杆的坐标x
21 var touchkey_y : float = movejoystick.position.y; //擷取操作杆的坐标y
22 //根據操縱杆的y位置計算汽車速度
23 frontleftwheel.motortorque = enginetorque / gearratio[currentgear] * touchkey_y;
24 frontrightwheel.motortorque = enginetorque / gearratio[currentgear] * touchkey_y;
25 //根據操縱杆的y位置計算汽車轉向
26 frontleftwheel.steerangle = 10 * touchkey_x;
27 frontrightwheel.steerangle = 10 * touchkey_x;
28 }else {//如果是其他平台
29 //計算汽車速度,當按下前進鍵或後退鍵,計算兩個前輪的馬達轉矩
30 frontleftwheel.motortorque = enginetorque / gearratio[currentgear] * input.getaxis("vertical");
31 frontrightwheel.motortorque = enginetorque / gearratio[currentgear] * input.getaxis("vertical");
32 //控制汽車轉彎,當按下左右按鍵時,計算兩個前輪的旋轉角度
33 frontleftwheel.steerangle = 10 * input.getaxis("horizontal");
34 frontrightwheel.steerangle = 10 * input.getaxis("horizontal");
35 }}
36 function shiftgears() {//轉換齒輪數的方法
37 if ( enginerpm >= maxenginerpm ) {//如果引擎轉速大于等于最大引擎速度
38 var appropriategear : int = currentgear;//定義一個合适齒輪數
39 for ( var i = 0; i < gearratio.length; i ++ ) {//周遊所有的齒輪數比
40 //如果左前輪轉速和齒輪數比的乘積小于最大引擎轉速
41 if ( frontleftwheel.rpm * gearratio[i] < maxenginerpm ) {
42 appropriategear = i;//指派
43 break;//跳出循環
44 }}
45 currentgear = appropriategear;//指派
46 }
47 if ( enginerpm <= minenginerpm ) {//如果引擎轉速小于等于最大引擎速度
48 appropriategear = currentgear;//指派
49 for ( var j = gearratio.length-1; j >= 0; j -- ) {//周遊所有的齒輪數比
50 //如果左前輪轉速和齒輪數比的乘積大于最小引擎轉速
51 if ( frontleftwheel.rpm * gearratio[j] > minenginerpm ) {
52 appropriategear = j;//指派
53 break;//跳出循環
54 }}
55 currentgear = appropriategear;//指派
56 }}