游戲開發中的物理之使用KinematicBody2D
@TOC
介紹
Godot提供了多個碰撞對象以提供碰撞檢測和響應。試圖確定要為您的項目使用哪個選項可能會造成混淆。如果您了解每個問題的工作原理和優點和缺點,則可以避免這些問題并簡化開發。在本教程中,我們將研究 KinematicBody2D節點,并顯示一些使用它的示例。
==注意==
本文檔假定您熟悉Godot的各種物理機構。請先閱讀物理簡介。
什么是運動機構?
KinematicBody2D用于實現通過代碼控制的主體。運動物體在移動時會檢測到與其他物體的碰撞,但不受重力或摩擦等發動機物理特性的影響。雖然這意味著您必須編寫一些代碼來創建其行為,但也意味著您可以更精確地控制它們的移動和反應方式。
提示
一個KinematicBody2D可以通過重力和其他力量的影響,但必須計算在代碼運動。物理引擎不會移動KinematicBody2D。
運動與碰撞
移動時KinematicBody2D,您不應position直接設置其屬性。而是使用move_and_collide()ormove_and_slide()方法。這些方法沿給定矢量移動物體,如果檢測到與另一個物體的碰撞,則立即停止。KinematicBody2D發生碰撞后,必須手動編碼任何碰撞響應。
警告
您只應在_physics_process()回調中進行運動身體運動。
兩種移動方法具有不同的用途,在本教程的后面,您將看到有關它們如何工作的示例。
move_and_collide
此方法采用一個參數:Vector2,指示人體的相對運動。通常,這是您的速度矢量乘以幀時間步(delta)。如果引擎在沿該矢量的任何位置檢測到碰撞,車身將立即停止移動。如果發生這種情況,該方法將返回KinematicCollision2D對象。
KinematicCollision2D是一個包含有關碰撞和碰撞對象的數據的對象。使用此數據,您可以計算碰撞響應。
move_and_slide
該move_and_slide()方法旨在簡化在您希望一個物體沿另一個物體滑動的常見情況下的碰撞響應。例如,它在平臺游戲或自上而下的游戲中特別有用。
提示
move_and_slide()使用會自動計算基于幀的移動delta。難道不是由乘你的速度矢量delta
它傳遞給前move_and_slide()。
除了速度矢量之外,還move_and_slide()可以使用許多其他參數來自定義滑動行為:
up_direction-默認值: Vector2( 0, 0 )
此參數允許您定義引擎應將哪些表面視為地板。設置這個可以讓你使用is_on_floor(),is_on_wall()和is_on_ceiling()方法來檢測機身的接觸是什么類型的表面。默認值表示所有表面均視為墻。
stop_on_slope-默認值: false
此參數可防止人體在站立時滑落斜坡。
max_slides-默認值: 4
此參數是身體停止移動之前的最大碰撞次數。設置得太低可能會完全阻止移動。
floor_max_angle-默認值:( 0.785398以弧度表示,等于45度)
此參數是在不再將表面視為“地板”之前的最大角度。
infinite_inertia-默認值: true
當此參數為時true,主體可以推動RigidBody2D 節點,而忽略其質量,但不會檢測到與它們的碰撞。如果是這樣,false 則身體將與剛體碰撞并停止。
move_and_slide_with_snap
此方法move_and_slide()通過添加snap參數來添加一些其他功能。只要此矢量與地面接觸,物體就會保持附著在地面上。請注意,例如,這意味著您必須在跳躍時禁用捕捉。您可以通過設置snap 為Vector2.ZERO或使用move_and_slide()來實現。
檢測碰撞
使用move_and_collide()該函數時KinematicCollision2D 直接返回一個,您可以在代碼中使用它。
在move_and_slide()計算滑動響應時,使用時可能會發生多次碰撞。要處理這些沖突,請使用get_slide_count() 和get_slide_collision():
# Using move_and_collide. var collision = move_and_collide(velocity * delta) if collision: print("I collided with ", collision.collider.name) # Using move_and_slide. velocity = move_and_slide(velocity) for i in get_slide_count(): var collision = get_slide_collision(i) print("I collided with ", collision.collider.name)
==注意==
get_slide_count()僅計數身體碰撞和改變方向的次數。
有關返回哪些碰撞數據的詳細信息,請參見KinematicCollision2D。
使用哪種運動方式?
Godot新用戶的一個常見問題是:“您如何決定使用哪種運動功能?” 通常,使用響應是move_and_slide()因為它“更簡單”,但不一定是這種情況。想到它的一種方法move_and_slide()是一種特殊情況,并且move_and_collide() 更為籠統。例如,以下兩個代碼段導致相同的碰撞響應:
// using MoveAndCollide var collision = MoveAndCollide(velocity * delta); if (collision != null) { velocity = velocity.Slide(collision.Normal); } // using MoveAndSlide velocity = MoveAndSlide(velocity);
您所做的任何事情都move_and_slide()可以使用來完成move_and_collide(),但可能需要花費更多的代碼。但是,正如我們在下面的示例中看到的那樣,在某些情況下move_and_slide()無法提供所需的響應。
在上面的示例中,我們將move_and_slide()返回的速度分配給velocity變量。這是因為當角色與環境碰撞時,該函數會在內部重新計算速度以反映速度下降。
例如,如果您的角色掉在地板上,您不希望它由于重力作用而積累垂直速度。相反,您希望其垂直速度重置為零。
move_and_slide()可能還會在一個循環中多次重新計算運動體的速度,因為要產生平滑運動,它將移動角色并默認碰撞最多5次。在過程結束時,該函數返回角色的新速度,該速度可以存儲在velocity 變量中,并用于下一幀。
例子
要查看這些示例,請下載示例項目: using_kinematic2d.zip。
機芯和墻壁
如果您下載了示例項目,則此示例位于“ BasicMovement.tscn”中。
對于此示例,添加KinematicBody2D具有兩個子元素的aSprite和a CollisionShape2D。使用Godot“ icon.png”作為Sprite的紋理(將其從Filesystem停靠拖到的Texture屬性Sprite)。在 CollisionShape2D“形狀”屬性中,選擇“新建RectangleShape2D”并調整矩形大小以適合精靈圖像。
==注意==
有關實施2D移動方案的示例,請參見2D移動概述。
將腳本附加到KinematicBody2D并添加以下代碼:
using Godot; using System; public class KBExample : KinematicBody2D { public int Speed = 250; private Vector2 _velocity = new Vector2(); public void GetInput() { // Detect up/down/left/right keystate and only move when pressed _velocity = new Vector2(); if (Input.IsActionPressed("ui_right")) _velocity.x += 1; if (Input.IsActionPressed("ui_left")) _velocity.x -= 1; if (Input.IsActionPressed("ui_down")) _velocity.y += 1; if (Input.IsActionPressed("ui_up")) _velocity.y -= 1; } public override void _PhysicsProcess(float delta) { GetInput(); MoveAndCollide(_velocity * delta); } }
運行此場景,您將看到它move_and_collide()按預期運行,沿速度矢量移動了身體。現在,讓我們看看添加一些障礙時會發生什么。添加具有矩形碰撞形狀的StaticBody2D。為了獲得可見性,可以使用sprite,Polygon2D或從“調試”菜單中打開“可見碰撞形狀”。
再次運行場景,然后嘗試移入障礙物。您會看到KinematicBody2D 無法穿透障礙物。但是,嘗試以一定角度移入障礙物,您會發現障礙物就像膠水一樣-感覺身體被卡住了。
發生這種情況是因為沒有碰撞響應。move_and_collide()發生碰撞時停止身體的運動。我們需要對碰撞產生的任何響應進行編碼。
嘗試將功能更改為move_and_slide(velocity)并再次運行。注意我們delta從速度計算中刪除了。
move_and_slide()提供沿碰撞對象滑動主體的默認碰撞響應。這對于許多游戲類型都非常有用,并且可能只是獲得所需行為的全部。
彈跳/反射
如果不想滑動碰撞響應怎么辦?對于此示例(示例項目中的“ BounceandCollide.tscn”),我們有一個射擊子彈的角色,我們希望這些子彈從墻上彈起。
本示例使用三個場景。主要場景包含播放器和墻壁。子彈頭和墻是分開的場景,因此可以被實例化。
播放器由w和s鍵控制前進和后退。瞄準使用鼠標指針。這是Player的代碼,使用move_and_slide():
using Godot; using System; public class KBExample : KinematicBody2D { private PackedScene _bullet = (PackedScene)GD.Load("res://Bullet.tscn"); public int Speed = 200; private Vector2 _velocity = new Vector2(); public void GetInput() { // add these actions in Project Settings -> Input Map _velocity = new Vector2(); if (Input.IsActionPressed("backward")) { _velocity = new Vector2(-Speed/3, 0).Rotated(Rotation); } if (Input.IsActionPressed("forward")) { _velocity = new Vector2(Speed, 0).Rotated(Rotation); } if (Input.IsActionPressed("mouse_click")) { Shoot(); } } public void Shoot() { // "Muzzle" is a Position2D placed at the barrel of the gun var b = (Bullet)_bullet.Instance(); b.Start(GetNode
和子彈的代碼:
using Godot; using System; public class Bullet : KinematicBody2D { public int Speed = 750; private Vector2 _velocity = new Vector2(); public void Start(Vector2 pos, float dir) { Rotation = dir; Position = pos; _velocity = new Vector2(speed, 0).Rotated(Rotation); } public override void _PhysicsProcess(float delta) { var collision = MoveAndCollide(_velocity * delta); if (collision != null) { _velocity = _velocity.Bounce(collision.Normal); if (collision.Collider.HasMethod("Hit")) { collision.Collider.Call("Hit"); } } } public void OnVisibilityNotifier2DScreenExited() { QueueFree(); } }
該動作發生在中_physics_process()。使用后move_and_collide(),如果發生沖突,KinematicCollision2D則返回一個對象(否則返回Nil)。
如果有返回的碰撞,我們使用的normal來velocity通過Vector2.bounce()方法反映子彈的碰撞。
如果碰撞對象(collider)具有hit方法,我們也將其稱為。在示例項目中,我們向“墻”添加了閃爍的色彩效果以演示這一點。
平臺運動
讓我們嘗試一個更流行的示例:2D平臺程序。move_and_slide() 是快速啟動和運行功能字符控制器的理想選擇。如果您已經下載了示例項目,則可以在“ Platformer.tscn”中找到它。
對于此示例,我們假設您有一個由StaticBody2D對象組成的關卡。它們可以是任何形狀和大小。在示例項目中,我們使用 Polygon2D創建平臺形狀。
這是播放器主體的代碼:
using Godot; using System; public class KBExample : KinematicBody2D { [Export] public int RunSpeed = 100; [Export] public int JumpSpeed = -400; [Export] public int Gravity = 1200; Vector2 velocity = new Vector2(); bool jumping = false; public void GetInput() { velocity.x = 0; bool right = Input.IsActionPressed("ui_right"); bool left = Input.IsActionPressed("ui_left"); bool jump = Input.IsActionPressed("ui_select"); if (jump && IsOnFloor()) { jumping = true; velocity.y = JumpSpeed; } if (right) velocity.x += RunSpeed; if (left) velocity.x -= RunSpeed; } public override void _PhysicsProcess(float delta) { GetInput(); velocity.y += Gravity * delta; if (jumping && IsOnFloor()) jumping = false; velocity = MoveAndSlide(velocity, new Vector2(0, -1)); } }
當使用時move_and_slide(),該函數返回一個向量,該向量表示發生滑動碰撞后剩余的運動。將該值重新設置為角色的值,velocity可以使我們平穩地上下傾斜。嘗試刪除并查看如果不這樣做會發生什么。velocity =
另請注意,我們已將其添加為下限法線。該向量指向正上方。結果,如果角色與具有該法線的對象碰撞,則將其視為地板。Vector2(0, -1)
使用地面法線可以使用進行跳躍工作is_on_floor()。此功能僅會返回true一個后move_and_slide()碰撞,其中碰撞體的法線是在45度定地板載體。您可以通過設置來控制最大角度floor_max_angle。
例如,該角度還允許您使用來實現其他功能,例如墻跳 is_on_wall()。
5G游戲
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。