一、什么是物理引擎?
四個世紀前,物理學家牛頓發現了萬有引力,並延伸出三大牛頓定理,為之后的物理學界的發展奠定了強大的理論基礎。牛頓有句話是這么說的:“如果說我看得比較遠的話,那是因為我站在巨人的肩膀上。”
日常工作生活中,我們寫文檔需要Word、Excel、Visio等辦公軟件,寫程序需要Visual Studio、Eclipse等各種集成開發環境、美術設計需要3dsMax、Maya、Photoshop等,正是因為有了這些強大的工具,我們的工作效率才會越來越高效。所以用記事本寫代碼、用Windows畫圖畫蒙娜麗莎什么的,只是個人能力的體現,保留一份敬佩的心情就好了,可別真應用到實際工作中哦,老板會瘋掉的,么么嗒!
話說不知道當年那個給牛頓帶來啟迪的蘋果,到底是掉在了地上還是真的掉在了牛頓的頭上,總之它就是往下掉了。運用基本的初中物理知識我們就知道這是因為地球對蘋果施加了重力。同樣的還有在“憤怒的小鳥”中,我們把小鳥用彈弓彈向空中之后,始終都會掉落在地面上。
那么,在Unity3D的游戲開發中,是通過什么機制來實現物體的物理效果的呢?我們把目光轉移到傳說中的物理引擎上。物理引擎通過為剛性物體(游戲中的具體游戲對象-GameObject)賦予真實的物理屬性的方式來模仿真實世界中的物體碰撞、跌落等反應。這里,我們可以簡單地理解為Unity3D默認幫我們實現了一個讓游戲對象具有真實物理對象的真實屬性(就像微軟給我們首先鋪墊了.NET Framework,而我們要做的就是在.NET Framework為我們提供的強大CLR和FCL之上編寫代碼實現具體的項目,而具體的類和對象怎么創建、分配內存、釋放資源和封裝方法我們一般都交給.NET Framework去處理)。例如游戲引擎中提供了Rigid Body(剛體)組件,為對象加入了該組件之后,游戲對象如果判斷腳下無支撐地面就會自動往下掉,就像當年從樹上掉落砸到牛頓童鞋的蘋果一樣。
簡而言之,物理引擎就是模擬真實世界中物體碰撞、跌落等反應的引擎。
擴展:Unity的物理引擎使用的是Nvidia的PhysX,PhysX 是一套由Nvidia設計的執行復雜的物理運算的技術。有關PhysX的相關信息請參閱:http://baike.baidu.com/link?url=kESZ9LXKgp-inbAF-tdjV3OUrwh9nxWnzqWBcGuXV51ightRio9evZ2nujXCnKdxlI3flDCWTwk2jXNH67KVb_
二、砸牛頓的蘋果—為游戲對象增加Rigid Body(剛體)
當一個游戲對象被賦予Rigid Body(剛體)組件之后,游戲引擎就會對其進行物理效果模擬。同時我們也可以給這個對象施加各種作用力,讓它運動起來。另外如果要實現重力的效果,那么相應的游戲物體都必須附上剛體組件。
那么,這里我們通過一個小例子來看看剛體組件的應用。
(1)在Hierarchy中Create以下游戲對象:一個Sphere,一個Direction Light,一個Plane(這里可以理解為地平面)。
首先將Plane的Position屬性設置為(0,-2,0),讓Plane顯示在Sphere的下方,作為Sphere跌落的地平面;
然后將Sphere的Position屬性設置為(0,2,0),讓Sphere顯示在Plane的上方,這樣Sphere能夠顯示一個完整的跌落效果;
最后將Main Camera的Position屬性設置為(0,0,-5),讓主視角能夠完整看到跌落效果,具體視角如下圖所以:
(2)在Hierarchy中選中Sphere,在菜單欄中選擇Component-Physics-RigidBody,便為Sphere增加了一個剛體組件。這下,這個Sphere便有了重力。點擊預覽按鈕,我們可以看到Sphere跌落的效果。(如果不增加Plane,Sphere會一直往下跌落,有興趣的朋友可以自己試試。)
(3)這里,可能有的讀者會說,一個球如果談到地上應該會自然地往上彈,彈一會之后才會平躺在地面上。也就是說,我們這個球不但應該具有重力,還應該具有彈力。沒事!物理引擎早就幫我們想好了。在資源管理器中的Assets上單擊鼠標右鍵,選擇Import Package-Physic Material(也就是導入一個物料材質的包),在彈出的選擇框中選中第一項Bouncy。
(4)在Assets中找到剛剛導入的Bouncy包,選中Bouncy並拖動到Hierarchy中的Sphere對象上,這樣就為Sphere增加了一個彈力的物理材質。也就是說,現在我們這個球可以在跌落到地面之后反彈了。
(5)這下,我們再預覽一下,看看效果:
(6)最后,我們來關注一下RigidBody的幾個屬性:
①Mass:質量—>學過物理的同學們都知道的吧,質量越大,慣性越大。這里的單位可以自己統一規定,但是官方給出的建議是場景中的物體質量最好不要相差100倍率以上。估計是防止兩個質量相差太大的物體碰撞后會產生過大的速度,從而影響游戲性能吧。
②Drug:阻力(也可以表示為摩擦力)—>這里指的是空氣阻力,當游戲物體收到某個作用力的時候,這個值越大越難移動。如果設置成無限的話,物體會立即停止移動。PS:上面那個Demo里邊我就將Sphere的Drug設置為了0.5。
③Angular Drag:角阻力—>同樣指的是空氣阻力,只不過是用來阻礙物體旋轉的。如果設置成無限的話,物體會立即停止旋轉。
④Use Gravity:是否使用重力—> 勾選了這個項,游戲對象就會受到重力影響。
⑤Is Kinematic:是否動態—>勾選這個選項會使游戲對象不受物理引擎的影響,但這不等同於沒有剛體組件。這通常用於需要用動畫控制的剛體,這樣就不會因為慣性而影響動畫了。
另外,還有Interplate(差值類型)、Collision Detection(碰撞檢測方式)以及Freeze Position/Rotation(凍結位置/旋轉),如果你有興趣,請參閱本文的參考文獻之一:《物理引擎:剛體與力》。
三、要重力更要“給力”—為游戲對象增加力
相信大家都玩過Angry Birds—憤怒的小鳥吧,我們在彈弓上將小鳥射出。根據我們對彈弓的不同力度不同角度,小鳥飛出的距離有長有短,力量有輕有重。
(1)在剛剛的Demo基礎上增加一個C# Script,命名為AddForce。雙擊該腳本文件,在Update方法中寫入以下代碼:
1 using UnityEngine; 2 using System.Collections; 3 4 public class AddForce : 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 // 判斷用戶是否按下了鼠標左鍵(0為左鍵,1為中建,2為右鍵) 17 if (Input.GetMouseButtonDown(0)) 18 { 19 // 參數一:朝哪個方向使力?使多大的力? 20 // 參數二:給什么類型的力? 21 this.gameObject.rigidbody.AddForce(Vector3.forward * 50, ForceMode.Impulse); 22 } 23 } 24 }
(2)編寫完成之后,還是老規矩:將腳本拖動到Sphere對象上進行綁定(也可以說是將腳本依附於游戲對象)。綁定完成之后,即可預覽該“給力”效果。在Sphere跌落到地平面的時候,點擊鼠標左鍵,該Sphere便迅速向前飛出。
PS:rigidbody.AddForce(Vector3.forward*50,ForceMode.Impulse)表示給一個向前(up為向上,down則為向下,back為向后)為50的力->所以Sphere會彈得那么有力!Impulse表示默示沖擊力。代碼中首先判斷用戶是否點擊了鼠標左鍵,如果點擊了則給Sphere對象增加一個向前的力。
(3)那么,看到這里也許玩過很多游戲的你會說:能不能讓小球朝着我鼠標指的方向飛呢?答案是肯定的,Unity3D早就為我們提供了這樣的方法,讓我們可以“指哪打哪”!這里就涉及到一個如何將鼠標所指示的屏幕坐標轉換為世界坐標(3D游戲中所能識別的正確坐標—NGUI坐標)的問題,在Unity中可以使用Camera.main.ScreenToWorldPoint(new Vector3(x,y,z))方法來實現。那么,現在就來實踐一下,修改剛剛的腳本代碼如下:
1 void Update() 2 { 3 // Demo1: 判斷用戶是否按下了鼠標左鍵(0為左鍵,1為中建,2為右鍵) 4 //if (Input.GetMouseButtonDown(0)) 5 //{ 6 // // 參數一:朝哪個方向使力?使多大的力? 7 // // 參數二:給什么類型的力? 8 // this.gameObject.rigidbody.AddForce(Vector3.forward * 50, ForceMode.Impulse); 9 //} 10 11 // Demo2:朝着用戶指定的區域為游戲對象給“力” 12 if (Input.GetMouseButtonDown(0)) 13 { 14 Vector3 screenSpace = Camera.main.WorldToScreenPoint(transform.position); 15 Vector3 targetPos = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, 16 Input.mousePosition.y, screenSpace.z)); 17 Vector3 dirPos = targetPos - Camera.main.transform.position; 18 19 this.gameObject.rigidbody.AddForce(dirPos * 10, ForceMode.Impulse); 20 } 21 }
(4)保存之后,再次預覽效果,差不多達到預期效果了,指哪打哪!
四、小結
本篇主要介紹了物理引擎最重要的剛體組件,它讓游戲對象一秒具有現實物理對象的重力效果。而為對象增加沖擊力,可以讓對象實現顯示對象中的沖擊效果,對於制作游戲有着重要的作用,也是后續篇制作打箱子游戲的基礎。轉眼之間,今天就是正月十四了,后天就要回學校了,回到成都了。抓緊時間,把最后一篇也是本系列初探Unity3D的完結篇完成,也算對得起自己這一段時間的成果了,同時也謝謝各位園友給我的鼓勵!最后,再謝謝楊中科老師在上一篇博客中給我的鼓勵,謝謝以下參考文獻的作者!
參考文獻與資料
(1)Unity3D基礎教程之組件介紹—物理引擎:http://www.narkii.com/club/thread-289321-1.html
(2)Unity3D學習筆記(十一):物理引擎之剛體與力:http://bbs.9ria.com/thread-186986-1-1.html
(3)傳智播客Unity3D公開課:http://net.itcast.cn/subject/Unity3D/index.html
(4)物理引擎-百度百科:http://baike.baidu.com/link?url=ff2cOmwhuHmwPPJT5ntk7_5UxqnUdQxrJuar3iLIzvM55ZEbCW3vS2SQ6nAYX4aD