游戲開發中的物理之使用KinematicBody2D
- 介紹
- 什么是運動機構?
- 運動與碰撞
- move_and_collide
- move_and_slide
- move_and_slide_with_snap
- 檢測碰撞
- 使用哪種運動方式?
- 例子
- 機芯和墻壁
- 彈跳/反射
- 平臺運動
介紹
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<Node2D>("Muzzle").GlobalPosition, Rotation);
GetParent().AddChild(b);
}
public override void _PhysicsProcess(float delta)
{
GetInput();
var dir = GetGlobalMousePosition() - GlobalPosition;
// Don't move if too close to the mouse pointer
if (dir.Length() > 5)
{
Rotation = dir.Angle();
_velocity = MoveAndSlide(_velocity);
}
}
}
和子彈的代碼:
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(),
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/237533.html
標籤:其他
