獨立遊戲開發中的物理系統
注:本文選自機械工業出版社出版的《獨立遊戲開發:基礎、實踐與創收》一書的小節,略有改動。經出版社授權刊登於此。
Unity物理系統更準確的叫法應該是物理引擎(Physics Engine),該引擎是採用NVIDIA的PhysX物理引擎實現的,為避免與遊戲引擎本身的名字衝突,本書還是稱其為物理系統。所謂物理系統,是指在遊戲物件上實現加速度、碰撞、重力、摩擦力及各種外力作用的一系列功能集合。Unity物理系統又分為2D和3D兩種型別,兩者在使用上大體相似,主要區別是3D物理系統多了一個維度。
Unity物理系統沒有總開關,只要在遊戲物件上附加並正確設定了物理元件(如Rigidbody、Collider、Joint、Effector等元件),即使用了物理系統功能。下面我們繼續開發案例遊戲,並基於物理系統實現主角的移動、跳躍、自由落體及更復雜的碰撞檢測等功能。
遊戲物件調整
對於Road,我們需要將其調整為一個有一定距離且主角可以站立的路面。首先將Road的Sprite Renderer元件Draw Mode屬性選擇為Tiled(在該屬性下,影像會根據遊戲物件尺寸自動填充,就像連續的瓦片一樣),然後在場景中拖曳Road的左右邊框(需要確保工具欄中的變換工具為Rect Tool狀態),適當增大其寬度後即可得到一個連續的路面。接下來調整碰撞器範圍,在Box Collider 2D元件中單擊Edit Collider按鈕後,碰撞器範圍即進入可編輯狀態,調整完後再次單擊Edit Collider按鈕即可。我們還需要取消碰撞器的Is Trigger屬性,以保證主角與路面的碰撞不可穿透,此時儘管Road並未附加Rigidbody 2D元件,但它相當於一個Static狀態的剛體。另外,之前的Road指令碼已經不適用了,我們將其對應指令碼元件從檢視視窗移除,並將該指令碼檔案從專案視窗刪除即可。如圖1和圖2所示:圖1展示了檢視視窗中Road的相關元件情況,標註框中為相關的調整項;圖2展示了Road在場景檢視中的情況,注意其碰撞器範圍是一個極細的矩形綠色框(圖中可能不容易看出來,請讀者結合實際操作檢視),我們將該範圍上邊框調整在Road高度二分之一的位置,對應馬路中央,也是遊戲角色的水平落腳點。
圖1 Road遊戲物件相關元件情況
圖2 調整後的Road遊戲物件
注:在圖1中,有一個三角形警告符號,其內容提示我們:當前本Sprite影像資源的匯入設定可能會造成Tiled模式下的繪製錯誤。但很明顯,我們這裡並未出現繪製錯誤,筆者在實際工作中也尚未遇到過此類錯誤,忽略該警告即可。或者,可在該Sprite的影像資源匯入設定中,將Mesh Type屬性設定為Full Rect以消除該警告。
對於Player,我們需要讓其擁有重力以及合適的碰撞範圍。首先將Rigidbody 2D元件的Gravity Scale屬性設定為4,以接受該值大小的重力等級。接著重新選擇碰撞器,由於主角有一個近似圓形的外觀,因此可用圓形的Circle Collider 2D元件替換Box Collider 2D元件,並適當調整其範圍大小,如圖3所示。
圖3 調整後的Player遊戲物件
對於RoadBlock,可用類似方法調整其碰撞範圍並刪除RoadBlock指令碼即可,具體步驟這裡不再贅述。
渲染順序修正
我們先執行遊戲,可以看到主角會因重力向路面下落,最終被錯誤地顯示在馬路後面,如圖4所示。要修正此問題,我們需要了解下Sprite Renderer元件的Sorting Layer與Order in Layer屬性:Sorting Layer屬性中可新增一系列特定名稱的排序分組,Unity將按照組順序依次渲染其中的Sprite;當多個Sprite同屬一個Sorting Layer分組時,則可通過Order in Layer屬性的值大小來決定它們的渲染順序。
值得注意的是,Soring Layer與Layer雖然只差一個單詞,但在Unity中它們是兩個不同的概念,可閱讀書中第5章中有關Layer的簡要介紹。另外讀者應知曉,Sprite Renderer元件功能不屬於物理系統功能。
下面,我們開始調整Sorting Layer與Order in Layer。首先,在Sorting Layer中新增一個Player分組:任意選擇一個遊戲物件,在檢視視窗中單擊Sorting Layer屬性右側的Default按鈕,並在展開的下拉選單中選擇Add Sorting Layer選項,即可開啟標籤與層的有關設定,其中,Sorting Layers欄下預設僅有一個Default分組,右下角的加減號(“+ -”)可增減分組,拖曳左側的等號(“=”)則可調整組順序,這裡我們新增一個Player分組,並保持現有順序即可。然後為Player的Sprite選擇該分組:單擊Player遊戲物件,直接將其Sorting Layer屬性右側的選項選擇為剛才新增的Player分組即可。接下來,Road與RoadBlock之間同樣需要調整渲染順序:將RoadBlock拖曳到Road上,可看到錯誤的前後關係,此時保持兩者的Sorting Layer同屬預設Default分組,我們保持Road的Order in Layer屬性為0,再將RoadBlock的Order in Layer屬性設為1,即可修正渲染順序(RoadBlock為0,Road為-1也可以)。圖5展示了調整後的執行效果。
圖4 錯誤的渲染順序
圖5 修正的渲染順序
基於物理系統的移動
這裡我們修改PlayerController指令碼,將當前基於Transform元件的移動替換為基於物理系統功能的移動,如程式碼1所示。
程式碼1 PlayerController.cs
- using UnityEngine;
- public class PlayerController : MonoBehaviour
- {
- // 用於引用Player的Rigidbody 2D元件
- private Rigidbody2D body;
- // 表示主角的移動速度
- public float speed;
- private void Start()
- {
- // 獲取Player的Rigidbody 2D元件
- body = GetComponent<Rigidbody2D>();
- }
- private void FixedUpdate()
- {
- KeyboardControl();
- }
- private void KeyboardControl()
- {
- // 通過鍵盤左右鍵輸入乘以速度變數得出水平速度
- float sp = speed * Input.GetAxis("Horizontal");
- // 根據水平速度和應有的垂直速度影響剛體速度
- body.velocity = new Vector2(sp, body.velocity.y);
- }
- }
上述程式碼中的GetComponent是一個泛型方法,用於獲取已附加元件,尖括號內為該元件的具體型別,這裡我們獲取Player的Rigidbody 2D元件,並由body變數引用。velocity是Rigidbody 2D元件中一個屬性,代表當前剛體的移動速度,我們把一個匿名二維向量賦值給它,即可實現剛體速度驅動遊戲物件的移動。在這個匿名二維向量中,X維度值即左右方向鍵輸入值與speed變數的積,Y維度值則對應剛體當前在該維度應有的速度(也就是說,我們僅控制水平速度,而不直接控制垂直速度,垂直速度將由外力實現,例如,下落時由物理系統重力產生向下的速度;跳躍時由人物跳躍力產生向上的速度)。
注:對精準名詞解釋一下,velocity表示速度,speed表示速率。速度是有方向和大小的向量,而速率是沒有方向的值。在沒有特別說明時,本書把兩者都稱為速度。
基於物理系統的跳躍與碰撞
我們繼續編寫PlayerController指令碼程式碼,為主角新增跳躍能力,並使用更復雜的碰撞檢測來判定其是否站立在地面上,如程式碼2所示。
程式碼2 PlayerController.cs
- using UnityEngine;
- public class PlayerController : MonoBehaviour
- {
- // 用於引用Player的Rigidbody 2D元件
- private Rigidbody2D body;
- // 表示主角的移動速度
- public float speed;
- private void Start()
- {
- // 獲取Player的Rigidbody 2D元件
- body = GetComponent<Rigidbody2D>();
- }
- private void FixedUpdate()
- {
- KeyboardControl();
- }
- private void KeyboardControl()
- {
- // 通過鍵盤左右鍵輸入乘以速度變數得出水平速度
- float sp = speed * Input.GetAxis("Horizontal");
- // 根據水平速度和應有的垂直速度影響剛體速度
- body.velocity = new Vector2(sp, body.velocity.y);
- }
- }
在上述程式碼中,我們新增了onGround與jumpPower變數,並使用了OnCollisionStay2D與OnCollisionExit2D方法。onGround變數是一個布林值,用於說明主角是否站立在地面上(或其他可站立物體上),當該值為真時,我們使用GetAxis("Vertical")獲取上下方向鍵輸入值,並在輸入值大於0時(向上的方向)呼叫AddForce方法在剛體上增加一個瞬時的力,該力是一個匿名二維向量,作為引數傳遞給AddForce方法,我們這裡只需要一個向上的力以產生向上的速度,因此該二維向量的X維度值設為0,Y維度值則設為代表跳躍力大小的jumpPower變數。
OnCollisionStay2D方法會在Player始終與碰撞物件接觸時連續執行,我們使用它檢測主角是否站立在地面上,其中,contactCount屬性儲存了Player與某個碰撞物件之間碰撞點的數量(多數情況下為1),我們用cnum變數儲存該數量,並結合for迴圈與GetContact方法遍歷所有的碰撞點;contact變數則用於依次儲存遍歷結果,每一個碰撞點都有一個normal屬性,該屬性是一個法線向量(這裡該向量是一個長度為1,並與Player碰撞點切線垂直的二維向量),當該向量Y維度值為1時,Player的碰撞點必然在正下方,即站立在地面上。這裡我們將該值的判定標準設為0.8,以考慮碰撞點稍稍偏離正下方的情況。OnCollisionExit2D方法會在Player脫離任意碰撞時執行,我們直接在其中將onGround變數賦值為假,即說明Player處於懸空狀態。
若此時執行遊戲,主角將會以滾動方式移動,這不是我們想要的效果。可在Rigidbody 2D元件中展開Constraints選項並勾選Freeze Rotation Z屬性以解決此問題,如圖6所示。最後在檢視視窗的Player Controller指令碼元件中,為Speed與Jump Power屬性分別設定一個合適的值(這裡我們設定為3和150),即可執行遊戲測試最終效果。
圖6 在Rigidbody 2D元件中鎖定Z軸旋轉
相關文章
- 獨立站運營——獨立站的3種搭建方式
- 互斥與獨立
- [提問交流]對onethink 中 獨立模型的改造模型
- MySQL中in(獨立子查詢)的執行計劃MySql
- 微信小程式系統獨立原始碼部署微信小程式原始碼
- win10怎麼啟用獨立顯示卡 win10系統獨立顯示卡設定方法Win10
- 獨立模型的相關需求模型
- 遊戲雜談:淺析很多獨立遊戲開發者堅持獨立而不願合作的原因遊戲開發
- 什麼是獨立IP,獨立IP主機怎麼樣?
- 代購系統獨立站的未來發展前景
- 獨立顯示卡與整合顯示卡的區別 獨立顯示卡與整合顯示卡哪個更好
- 使用simg2img win提取安卓官方ROM包中獨立的系統軟體安卓
- 獨立開啟Oracle的Standby庫Oracle
- 獨立開發者的程式碼簽名
- WPF打包獨立執行的程式
- 初探 Cocos Creator: 碰撞與物理系統
- Win10怎麼禁用獨立顯示卡 win10獨立顯示卡的關閉方法Win10
- 獨立站如何批次查收錄,教你獨立站如何批次查收錄的簡單方法
- 遺世獨立的元件——Angular應用中的單元件構建元件Angular
- win10系統開啟獨立顯示卡的方法【圖文】Win10
- 電腦的獨立顯示卡是幹什麼用的 不玩遊戲有必要買獨立顯示卡嗎遊戲
- win10系統禁用獨立顯示卡在哪設定_win10禁用獨立顯示卡怎麼設定Win10
- 拒絕Epic Games Store獨佔協議的獨立開發者GAM協議
- 獨立模型 和分類模型
- 獨立開發挑戰
- idea 獨立視窗Idea
- 採集Prestashop獨立站REST
- 什麼是獨立ip
- 獨立網站SEO之路網站
- angularJS獨立作用域AngularJS
- WebBrowser獨立IE使用代理Web
- 獨立顯示卡與整合顯示卡的區別 獨立顯示卡與整合顯示卡優缺點介紹
- 獨立站如何批次查收錄,教你獨立站如何批次查收錄的方法操作步驟
- 數藏nft系統開發原始碼獨立部署原始碼
- 永春堂系統開發原始碼獨立部署原始碼
- 獨立商城系統、原始碼、報價之探究_OctShop原始碼
- 獨立開發者創業過程中會犯的14個錯誤創業
- 一個獨立開發者的失敗自白