一、預備知識—對象的”生“與”死“
(1)如何在游戲腳本程序中創建對象而不是一開始就創建好對象?->使用GameObject的靜態方法:CreatePrimitive()
以上一篇的博文中的“指哪打哪”例子為基礎,在AddForce腳本寫入以下代碼:

1 void Update() 2 { 3 // Demo3:點擊鼠標左鍵自動創建Cube對象 4 if (Input.GetMouseButtonDown(0)) 5 { 6 CreateCube(); 7 } 8 } 9 10 void CreateCube() 11 { 12 GameObject goCube = GameObject.CreatePrimitive(PrimitiveType.Cube); 13 goCube.transform.position = new Vector3(0, 0, 0); 14 goCube.AddComponent<Rigidbody>(); 15 }
其中在CreateCube方法中,使用GameObject.CreatePrimitive方法來創建Cube類型的游戲對象實例,設置了它出現的坐標並為它增加剛體組件。這里可以看下AddComponent方法,它的參數是一個泛型,也就是說我們在屬性面板中看到的那些組件,例如剛體、音頻源甚至腳本等組件對象都可以通過AddComponet方法來動態地添加。現在來看看在游戲中點擊鼠標左鍵創建Cube對象的效果:
(2)細心的讀者會發現,當我們創建了無數個Cube對象之后,計算機的內存占用率會逐步上升。機智的你肯定會想到,適時銷毀創建的游戲對象,釋放內存資源。不要擔心,Unity3D為我們提供了一個非常方便的方法:Destroy()。這個函數提供了兩個重載:第一個你可以直接傳遞一個游戲對象的ID(比如我們在上個例子中創建了一個Plane,它的ID也為Plane);第二個你可以傳遞兩個參數,一個是剛剛提到的游戲對象的ID,另一個是延遲銷毀的秒數(也就是說可以在規定的秒數之后再從屏幕中消失,從內存中銷毀);
下面我們重新修改一下剛剛的AddForce腳本為如下代碼:

1 void Update() 2 { 3 4 // Demo4:點擊鼠標左鍵自動銷毀Plane對象 5 if(Input.GetMouseButtonDown(0)) 6 { 7 DestroyGameObject("Plane"); 8 } 9 } 10 11 void DestroyGameObject(string objectName) 12 { 13 GameObject goCube = GameObject.Find(objectName); 14 // 延時2秒之后才銷毀該對象 15 Destroy(goCube, 2); 16 }
這里我們將銷毀游戲對象的代碼封裝成了一個方法:DestroyGameObject(),它首先通過GameObject.Find方法找到指定ID的游戲對象,然后調用銷毀游戲對象的方法Destroy將其銷毀掉,這里使用了第二個重載,為其傳遞了一個2秒的延遲時間。也就是說,當我們點擊鼠標左鍵后2秒,Plane對象才會被銷毀,我們的Sphere球體對象便會跌落深淵!現在我們來看看效果如何:
在預覽效果的同時,你可以觀察左側Hierarchy中的對象列表,Plane在什么時刻消失了?
好了,預備知識到此結束,現在我們真正開始CrazySphere(簡稱:CS,聽起來高大上吧,么么嗒!)—“瘋狂擊箱子”游戲的開發之旅!
二、瘋狂擊箱子—CrazySphere的實現之路
既然我們的“CS”是擊箱子,木有箱子怎么能行呢!現在,我們就來創建一些箱子,作為我們擊打的對象。
首先,考慮到要初始化的箱子足足有16個,我們需要在程序中來創建這些箱子,並將它們放在Plane中,組成箱子牆,讓我們的小球來擊打。
(1)在Hierarchy中Create以下對象:一個Direction Light,一個Plane;將Plane的Position設置為(0,1,-6),這樣看起來清楚一點;
(2)准備工作:
①在Assets中Create以下文件夾(Folder):一個Images文件夾用來存放貼圖文件,一個Musics文件夾用來存放背景音樂和音效MP3,一個Scripts文件夾用來存放C#腳本文件,見下圖所示:
②往Images里邊導入一些貼圖,我這里在網上找了幾張憤怒的小鳥的圖片,用來給Plane、Sphere以及Cube對象做渲染材質貼圖,這樣游戲對象看起來好看一點。(具體的文件請下載附件中的源碼,找到Assets/Images/)
③往Musics里邊導入一首背景音樂和一首音效文件,背景音樂作為游戲背景音樂默認且循環播放,音效音樂作為小球沖擊箱子的音效在小球發出時播放。這里背景音樂我選擇的是薩克斯經典—回家,是不是很文藝?音效呢,我就上網隨便找了個炮彈發射的音效。(具體的文件請下載附件中的源碼,找到Assets/Musics/)
然后,選中Hierarchy中的Main Camera,選擇主菜單欄中的Component->Audio->Audio Source,在屬性中的Audio Source塊中選擇導入的背景音樂(這里是GoHome-Sax),並勾選Play On Awake(是否默認播放)以及Loop(是否循環播放)復選框,我們在游戲一開始時就會播放GoHome-Sax.mp3文件。
最后,按照上面的步湊為Plane增加Audio Source,將其選擇為Bomb作為初始化音效。
④在Scripts中Create兩個C# Script,一個命名為InitScene,另一個命名為AutoDestroy。InitScene腳本用於初始化游戲場景,也就是4*4的箱子矩陣。而AutoDestroy腳本則用於銷毀超出主攝像機可視范圍的游戲對象,也就是當我們的小球或被擊中的箱子超出Plane的地面范圍或跌落后就將其自動銷毀。
(3)首先來編寫AutoDestroy腳本,利用我們在 預備知識 里邊學到的自動銷毀對象的方法。這個AutoDestroy腳本是需要附加到需要自動銷毀的游戲對象上才會有意義,所以后邊會在初始化場景的主腳本中為自動創建的對象附加此腳本(利用AddComponent提供的泛型方法)。

1 using UnityEngine; 2 using System.Collections; 3 4 public class AutoDestroy : MonoBehaviour 5 { 6 7 // Use this for initialization 8 void Start() 9 { 10 11 } 12 13 // Update is called once per frame 14 void Update() 15 { 16 17 } 18 19 // 當離開攝像頭可視范圍時觸發的事件 20 void OnBecameInvisible() 21 { 22 // 銷毀當前游戲對象 23 Destroy(this.gameObject); 24 } 25 }
PS:OnBecameInvisible()方法是Unity3D中自帶的方法,它在具體的游戲對象在游戲屏幕上不可見時觸發。你可以理解它就類似於ASP.NET WebForm中Global文件中的Application_End()事件。這里,我們在游戲對象不可見時,銷毀具體的游戲對象。注意,這里銷毀的方法參數是this.GameObject而不是this!
(4)現在我們來編寫InitScene腳本,這個是重點!編寫完成后,把此腳本附加到Main Camera對象中!

1 using UnityEngine; 2 using System.Collections; 3 4 public class InitScene : MonoBehaviour 5 { 6 private GameObject goPlane; 7 8 // Use this for initialization 9 void Start() 10 { 11 // 獲取Plane對象 12 goPlane = GameObject.Find("Plane"); 13 // 初始化4*4個箱子群 14 CreateCubes(); 15 } 16 17 void CreateCubes() 18 { 19 // 創建4*4個Cube立方體作為箱子 20 for (int i = 0; i < 4; i++) 21 { 22 for (int j = 0; j < 4; j++) 23 { 24 GameObject goCube = GameObject.CreatePrimitive(PrimitiveType.Cube); 25 goCube.transform.position = new Vector3(i - 1, j, -1); 26 // 增加剛體組件使其具有重力 27 goCube.AddComponent<Rigidbody>(); 28 // 增加腳本使對象自動銷毀 29 goCube.AddComponent<AutoDestroy>(); 30 // 增加渲染貼圖 31 goCube.renderer.material.mainTexture = 32 Resources.LoadAssetAtPath("Assets/Images/CubeImage.jpg", typeof(Texture)) as Texture; 33 } 34 } 35 } 36 37 // Update is called once per frame 38 void Update() 39 { 40 // 控制Sphere朝着鼠標指定的坐標發起沖擊 41 if (Input.GetMouseButtonDown(0)) 42 { 43 // 創建要發出的小球Sphere 44 GameObject goBullet = GameObject.CreatePrimitive(PrimitiveType.Sphere); 45 goBullet.transform.position = Camera.main.transform.position; 46 goBullet.AddComponent<Rigidbody>(); 47 goBullet.AddComponent<AutoDestroy>(); 48 goBullet.renderer.material.mainTexture = 49 Resources.LoadAssetAtPath("Assets/Images/AngryBird.jpg", typeof(Texture)) as Texture; 50 51 // 獲取目標位置的世界坐標 52 Vector3 targetPos = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, 53 Input.mousePosition.y, 3)); 54 Vector3 dirPos = targetPos - Camera.main.transform.position; 55 56 // 進擊吧!瘋狂的小球! 57 goBullet.rigidbody.AddForce(dirPos * 10, ForceMode.Impulse); 58 59 // 播放爆炸音效 60 goPlane.GetComponent<AudioSource>().Play(); 61 } 62 } 63 64 }
現在我們一一來分析這段腳本代碼:
①CreateCubes()方法定義了初始化4*4個箱子的實現過程,每循環一次通過CreatePrimitive創建Cube類型的立方體,然后為每個立方體設置position坐標、增加剛體組件、增加腳本使其能夠自動銷毀以及為其渲染貼圖。
②在Update()方法中控制小球朝着鼠標指定的坐標發起沖擊:當用戶點擊鼠標左鍵時即刻創建一個Sphere小球,仍然是設置坐標、增加剛體組件、渲染貼圖、增加腳本使其能夠自動銷毀。這里需要注意的是,小球的坐標應該為攝像頭的位置,因為小球是從攝像頭飛出去的。然后,通過屏幕坐標向世界坐標的轉換獲取目標向量,再通過目標所在向量-攝像頭所在向量=方向向量(這里涉及到向量減法,不明白的讀者可以看看本文第二篇3D模型基礎,或者去復習下高中向量減法的幾何意義)。最后,為小球添加一個往鼠標點擊的方向的多大的力,它就會往那個方向去走(這里是“飛“)。為了突出效果,這里還為小球添加了音效效果,在發出時播放。
(5)到這里,一個基本的CrazySphere就可以實現了,現在我們來看下效果:可以讓小球按照我們制定的坐標發射,發射時還會有炮彈的音效,而且背景音樂一直在循環播放着,一個demo就差不多完成了,是不是很快!
(6)但是大家是否覺得我們的游戲背景太單調了,沒關系,Unity3D為我們提供了Skyboxes-天空盒子,讓我們的背景一秒變為燦爛的藍天!(有關天空盒子的詳細內容請參閱參考文獻中關於天空盒子的介紹,這里不再闡述)這里我們向場景中添加一個Sunny的天空盒子:
①在Assets處單擊鼠標右鍵,選擇Import Package->Skyboxes,在彈出的選擇框中選擇Sunny1的mat、與Sunny1有關的tif資源。這里注意不要將全部的天空盒子都導進來,那樣文件會很大!
②點擊主菜單欄Edit->Render Settings,在右側的屬性欄中找到Skybox Material:
單機右側的選擇按鈕,在彈出的選擇框中即可看到我們剛剛導入的Sunny1這個天空盒子,雙機選中它,這樣我們就讓游戲背景一秒變為陽光燦爛的藍天,是不是心曠神怡啊!
(6)現在,我們再來看看游戲效果:是不是變為藍天啦?這樣,我們的CrazySphere v1.0就開發好了!
三、總結
通過幾天的Unity3D初探學習,我們學習了Unity3D的基本知識、3D模型基礎、物理引擎基礎,並綜合這些知識做了一個小游戲:CrazySphere-瘋狂擊箱子的游戲,還實現了背景音樂、音效效果的播放,加入天空盒子讓游戲背景好看。當然,本系列作為初探,不可能學的很深入,但至少我們可以入門,可以初步領略到Unity3D的強大魅力以及我們.NET程序員的學習優勢。
不知不覺之間,已經寫了四篇關於Unity3D的學習筆記了,同時這也是我的第一個系列的博文,對我的博客生涯具有重要的意義,再次感謝給我鼓勵的園友們,讓我作為一個新人倍感榮幸。另外,本文是基於傳智播客的Unity3D的兩次公開課為基礎,整理而成的,衷心感謝傳智播客以及楊中科的分享,還有老楊的鼓勵。馬上就要開學了,又要回成都了,苦逼的研究生生涯還得繼續,好想早點畢業啊!被學校派到外邊實習,老師(實驗室指導老師,非我的導師,我的導師還是蠻不錯的)也不准時發工資,每天還干的累死累活的。但是,還是想在此祝願各位園友碼年吉祥,2014越碼越健康!
明天就是我外婆70歲的生日了,在此也祝願她老人家生日快樂,身體健康!
本文最后會附上本次小游戲案例的Demo文件供下載,里面有本次用到的所有素材。另外,該Demo中還使用了GUI自定義了鼠標顯示,將鼠標顯示替換為一張瞄准星的貼圖,如下圖所示:
參考文獻與資料
(1)傳智播客Unity3D公開課:http://net.itcast.cn/subject/Unity3D/index.html
(2)XieXuan2007,《Unity3D天空盒》:http://blog.csdn.net/xiexuan2007/article/details/18401075
(3)丁小未,《Unity3D開發類似保齡球游戲》:http://blog.csdn.net/dingxiaowei2013/article/details/9734935
附件
Demo文件下載:https://github.com/EdisonChou/CrazySphere