游戲開發中的物理介紹
在游戲開發中,您通常需要知道游戲中的兩個對象何時相交或接觸。這就是所謂的碰撞檢測。當檢測到碰撞時,您通常希望發生某些事情。這就是所謂的碰撞響應。
Godot在2D和3D中提供了許多碰撞對象,以提供碰撞檢測和響應。試圖確定要為您的項目使用哪個選項可能會造成混淆。如果您了解每個工作原理以及各自的優缺點,則可以避免問題并簡化開發。
在本指南中,您將學習:
戈多的四種碰撞對象類型
每個碰撞對象如何工作
什么時候以及為什么要選擇一種而不是另一種
==注意==
本文檔的示例將使用2D對象。每個2D物理對象和碰撞形狀在3D中具有直接等效的功能,并且在大多數情況下,它們的工作方式幾乎相同。
碰撞對象
Godot提供了四種物理體,擴展了CollisionObject2D:
Area2D
Area2D節點提供檢測和影響。它們可以檢測物體何時重疊,并可以在物體進入或離開時發出信號。Area2D 還可以使用an來覆蓋定義區域中的物理特性,例如重力或阻尼。
其他三個主體擴展了PhysicsBody2D:
StaticBody2D
靜態物體是物理引擎不會移動的物體。它參與碰撞檢測,但不會響應碰撞而移動。它們最常用于環境中的對象或不需要任何動態行為的對象。
RigidBody2D
這是實現模擬2D物理的節點。您無需RigidBody2D直接控制a ,而是要對其施加力(重力,脈沖等),然后物理引擎將計算最終的運動。閱讀更多有關使用剛體的信息。
KinematicBody2D
提供碰撞檢測但沒有物理學的物體。所有運動和碰撞響應都必須用代碼實現。
碰撞形狀
物理物體可以將任意數量的Shape2D對象作為子對象。這些形狀用于定義對象的碰撞范圍并檢測與其他對象的接觸。
==注意==
為了檢測碰撞,Shape2D必須至少分配一個對象。
分配形狀的最常見方法是添加CollisionShape2D 或CollisionPolygon2D作為對象的子級。這些節點允許您直接在編輯器工作區中繪制形狀。
重要
注意不要在編輯器中縮放碰撞形狀。 檢查器中的“比例”屬性應保留為(1,1)。
更改碰撞形狀的大小時,應始終使用大小控制柄,而不是Node2D比例控制柄。 縮放形狀會導致意外的碰撞行為。
物理過程回調
物理引擎可以產生多個線程以提高性能,因此它最多可以使用一個完整的幀來處理物理。 因此,對于當前幀,身體的狀態變量(例如位置或線速度)的值可能不準確。
為了避免這種不準確性,任何需要訪問人體屬性的代碼都應在Node._physics_process() 回調中運行,該回調在每個物理步驟之前以恒定幀速率(默認為每秒60次)被調用。該方法將被傳遞一個delta 參數,該參數是一個浮點數,它等于自上一步以來經過的時間(以 秒為單位)。當使用默認的60 Hz物理更新速率時,通常等于0.01666…(但不總是如此,請參見下文)。
==注意==
建議始終delta在物理計算中使用相關參數,以便在您更改物理更新率或玩家的設備無法跟上時,游戲能夠正確運行。
碰撞層和蒙版
碰撞層系統是最強大但經常被誤解的碰撞特征之一。該系統使您可以在各種對象之間建立復雜的交互。關鍵概念是圖層 和蒙版。每個CollisionObject2D都有可與之交互的20個不同的物理層。
讓我們依次查看每個屬性:
碰撞層
這描述了對象出現在的層。默認情況下,所有實體都在layer上1。
碰撞面罩
這描述了身體將掃描碰撞的層。如果對象不在遮罩層之一中,則主體將忽略它。默認情況下,所有實體都掃描layer 1。
這些屬性可以通過代碼或在檢查器中編輯來配置。
跟蹤每個圖層的用途可能很困難,因此您可能會發現為使用的圖層分配名稱很有用。可以在項目設置->圖層名稱中分配名稱。
GUI示例
游戲中有四種節點類型:墻,玩家,敵人和硬幣。玩家和敵人都應與墻碰撞。播放器節點應同時檢測到與敵人和硬幣的碰撞,但敵人和硬幣應互相忽略。
首先命名第1-4層“墻”,“玩家”,“敵人”和“硬幣”,然后使用“層”屬性將每個節點類型放置在其相應的層中。然后通過選擇每個節點應與之交互的層來設置每個節點的“蒙版”屬性。例如,播放器的設置如下所示:
代碼示例
在函數調用中,將圖層指定為位掩碼。如果功能默認啟用所有圖層,則圖層蒙版將指定為0x7fffffff。您的代碼可以對圖層蒙版使用二進制,十六進制或十進制表示法,具體取決于您的偏好。
上面啟用了第1、3和4層的示例的等效代碼如下:
# Example: Setting mask value for enabling layers 1, 3 and 4 # Binary - set the bit corresponding to the layers you want to enable (1, 3, and 4) to 1, set all other bits to 0. # Note: Layer 20 is the first bit, layer 1 is the last. The mask for layers 4,3 and 1 is therefore 0b00000000000000001101 # (This can be shortened to 0b1101) # Hexadecimal equivalent (1101 binary converted to hexadecimal) 0x000d # (This value can be shortened to 0xd) # Decimal - Add the results of 2 to the power of (layer be enabled-1). # (2^(1-1)) + (2^(3-1)) + (2^(4-1)) = 1 + 4 + 8 = 13 pow(2, 1) + pow(2, 3) + pow(2, 4)
Area2D
區域節點提供檢測和影響。它們可以檢測物體何時重疊并在物體進入或離開時發出信號。區域還可以用于覆蓋定義區域中的物理屬性,例如重力或阻尼。
Area2D有三個主要用途:
給定區域中的替代物理參數(例如重力)。
檢測其他物體何時進入或離開區域或當前區域中有哪些物體。
檢查其他區域是否重疊。
默認情況下,區域還接收鼠標和觸摸屏輸入。
StaticBody2D
靜態物體是物理引擎不會移動的物體。它參與碰撞檢測,但不會響應碰撞而移動。但是,它可以利用其和屬性為碰撞的物體提供運動或旋轉,就好像它在運動一樣。constant_linear_velocityconstant_angular_velocity
StaticBody2D 節點最常用于環境中的對象或不需要任何動態行為的對象。
示例用于StaticBody2D:
平臺(包括移動平臺)
輸送帶
墻壁和其他障礙
RigidBody2D
這是實現模擬2D物理的節點。您不能直接控制 RigidBody2D。取而代之的是,您對其施加力,然后物理引擎會計算出最終的運動,包括與其他物體的碰撞以及碰撞響應(如彈跳,旋轉等)。
您可以通過“質量”,“摩擦”或“彈跳”之類的屬性來修改剛體的行為,這些屬性可以在檢查器中設置。
人體的行為也會受到世界屬性(如在“ 項目設置”->“物理”中設置的)的影響,或者受輸入 覆蓋全球物理屬性的Area2D的影響。
當剛體處于靜止狀態并且一段時間未移動時,它將進入睡眠狀態。睡眠物體的作用類似于靜態物體,其力不是由物理引擎計算的。當通過碰撞或通過代碼施加力時,身體將醒來。
剛體模式
剛體可以設置為以下四種模式之一:
剛性-身體表現為物理對象。它會與其他物體碰撞,并對其施加的力作出反應。這是默認模式。
靜態-主體的行為類似于StaticBody2D,并且不會移動。
角色-與“剛性”模式相似,但身體無法旋轉。
運動-身體的行為類似于KinematicBody2D,必須通過代碼移動。
使用RigidBody2D
使用剛體的好處之一是無需編寫任何代碼即可“免費”獲得許多行為。例如,如果您要制作帶有下降塊的“憤怒的小鳥”式游戲,則只需創建RigidBody2Ds并調整其屬性。堆積,下落和彈跳將由物理引擎自動計算。
但是,如果你想有過身體有一定的控制,你應該照顧-改變position,linear_velocity,剛體的或其他物理性質可能會導致意外的行為。如果您需要更改任何與物理學相關的屬性,則應使用_integrate_forces() 回調而不是_physics_process()。在此回調中,您可以訪問人體的Physics2DDirectBodyState,它可以安全地更改屬性并將其與物理引擎同步。
例如,以下是“小行星”式太空飛船的代碼:
class Spaceship : RigidBody2D { private Vector2 _thrust = new Vector2(0, 250); private float _torque = 20000; public override void _IntegrateForces(Physics2DDirectBodyState state) { if (Input.IsActionPressed("ui_up")) SetAppliedForce(_thrust.Rotated(Rotation)); else SetAppliedForce(new Vector2()); var rotationDir = 0; if (Input.IsActionPressed("ui_right")) rotationDir += 1; if (Input.IsActionPressed("ui_left")) rotationDir -= 1; SetAppliedTorque(rotationDir * _torque); } }
請注意,我們不是 直接設置linear_velocity或angular_velocity屬性,而是將力(thrust和torque)施加到物體上,然后讓物理引擎計算出所產生的運動。
注意
當剛體進入睡眠狀態時,_integrate_forces()
將不會調用該功能。要覆蓋此行為,您將需要通過創建碰撞,向其施加力或禁用can_sleep
屬性來使身體保持清醒狀態。請注意,這可能會對性能產生負面影響。
聯系人報告
默認情況下,剛體不跟蹤接觸,因為如果場景中有很多剛體,這可能需要大量的內存。若要啟用聯系人報告,請將contacts_reported 屬性設置為非零值。然后可以通過Physics2DDirectBodyState.get_contact_count()和相關函數來獲取聯系人 。
可以通過contact_monitor 屬性啟用通過信號的接觸監視。有關可用信號的列表,請參見RigidBody2D。
KinematicBody2D
KinematicBody2D實體可以檢測與其他實體的碰撞,但不受重力或摩擦等物理屬性的影響。相反,它們必須由用戶通過代碼控制。物理引擎不會移動運動體。
移動運動機構時,請勿position直接設置它。而是使用move_and_collide()ormove_and_slide()方法。這些方法沿著給定的矢量移動物體,如果檢測到與另一個物體的碰撞,它將立即停止。身體碰撞后,任何碰撞響應都必須手動編碼。
運動碰撞響應
發生碰撞后,您可能希望身體反彈,沿墻滑動或改變其撞擊的對象的屬性。處理碰撞響應的方式取決于您用來移動KinematicBody2D的方法。
move_and_collide
使用時move_and_collide(),該函數返回 KinematicCollision2D對象,該對象包含有關碰撞和碰撞體的信息。您可以使用此信息來確定響應。
例如,如果要查找空間中發生碰撞的點:
class Body : KinematicBody2D { private Vector2 _velocity = new Vector2(250, 250); public override void _PhysicsProcess(float delta) { var collisionInfo = MoveAndCollide(_velocity * delta); if (collisionInfo != null) { var collisionPoint = collisionInfo.GetPosition(); } } }
或從碰撞對象反彈:
class Body : KinematicBody2D { private Vector2 _velocity = new Vector2(250, 250); public override void _PhysicsProcess(float delta) { var collisionInfo = MoveAndCollide(_velocity * delta); if (collisionInfo != null) _velocity = _velocity.Bounce(collisionInfo.Normal); } } move_and_slide
滑動是一種常見的碰撞反應。想象一個玩家在自上而下的游戲中沿著墻壁移動,或者在平臺游戲中在斜坡上上下移動。雖然可以使用后,自己編寫這種反應move_and_collide(), move_and_slide()提供了實現滑動,而無需編寫大量代碼的便捷方式。
警告
move_and_slide()自動包括在計算中時間步長,所以應該沒有乘法的速度矢量通過delta。
例如,使用以下代碼制作一個可以在地面上行走(包括斜坡)并在站立在地面上時可以跳躍的角色:
class Body : KinematicBody2D { private float _runSpeed = 350; private float _jumpSpeed = -1000; private float _gravity = 2500; private Vector2 _velocity = new Vector2(); private void GetInput() { _velocity.x = 0; var right = Input.IsActionPressed("ui_right"); var left = Input.IsActionPressed("ui_left"); var jump = Input.IsActionPressed("ui_select"); if (IsOnFloor() && jump) _velocity.y = _jumpSpeed; if (right) _velocity.x += _runSpeed; if (left) _velocity.x -= _runSpeed; } public override void _PhysicsProcess(float delta) { _velocity.y += _gravity * delta; GetInput(); _velocity = MoveAndSlide(velocity, new Vector2(0,-1)); } }
有關使用的更多詳細信息,請參見運動字符(2D)move_and_slide(),包括帶有詳細代碼的演示項目。
5G游戲
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。