Up to date
This page is up to date for Godot 4.2
.
If you still find outdated information, please open an issue.
產生實體與訊號¶
訊號提供了一種解耦遊戲物件的方式, 讓你避免強行強制固定節點排列. 當使用 get_parent()
時, 你會發現可能需要呼叫訊號的一個旗標, 直接引用一個節點的父節點意味著你不能輕易地將該節點移動到場景樹的另一個位置. 當你在運作時產生實體物件, 並且可能想把它們放在運作中的場景樹的任意位置時, 這可能是個問題.
下面我們將考慮這種情況的一個例子: 發射子彈.
射擊範例¶
考慮一個可以旋轉並向滑鼠射擊的玩家角色. 每次點擊滑鼠按鈕, 我們就在玩家的位置建立一個子彈的實例. 詳見 編輯實體 .
我們用 Area2D
來表示子彈, 它以給定的速度直線運動:
extends Area2D
var velocity = Vector2.RIGHT
func _physics_process(delta):
position += velocity * delta
using Godot;
public partial class Bullet : Area2D
{
public Vector2 Velocity { get; set; } = Vector2.Right;
public override void _PhysicsProcess(double delta)
{
Position += Velocity * (float)delta;
}
}
然而, 如果子彈作為遊戲角色的子節點新增, 那麼當它旋轉時, 子彈將仍然保持 "附著" 在遊戲角色身上:
相反, 我們需要子彈獨立於遊戲角色的移動——一旦發射, 子彈將持續沿著直線運動, 遊戲角色就不能再影響它們了. 與其作為遊戲角色的子節點被新增到場景樹中, 不如將子彈作為 "主" 遊戲場景的子節點新增上去更有意義, 後者可能是遊戲角色的父節點, 甚至可能是更高層級的樹.
你可以通過將子彈直接新增到主場景中來做到這一點:
var bullet_instance = Bullet.instantiate()
get_parent().add_child(bullet_instance)
Node bulletInstance = Bullet.Instantiate();
GetParent().AddChild(bulletInstance);
然而, 這將導致一個不同的問題. 現在, 如果你試圖獨立測試你的 "Player" 場景, 它將在射擊時當機, 因為沒有父節點可以存取. 這使得獨立測試你的遊戲角色程式碼變得更加困難, 也意味著如果你決定改變你的主場景的節點結構, 遊戲角色的父節點可能不再是接收子彈的適當節點.
解決這個問題的方法是使用一個訊號來 "發射" 遊戲角色的子彈. 遊戲角色不需要 "知道" 子彈在那之後發生了什麼——任何連接到訊號的節點都可以 "接收" 子彈並採取適當的行動來產生它們.
下面是遊戲角色使用訊號發射子彈的程式碼:
extends Sprite2D
signal shoot(bullet, direction, location)
var Bullet = preload("res://bullet.tscn")
func _input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
shoot.emit(Bullet, rotation, position)
func _process(delta):
look_at(get_global_mouse_position())
using Godot;
public partial class Player : Sprite2D
{
[Signal]
public delegate void ShootEventHandler(PackedScene bullet, float direction, Vector2 location);
private PackedScene _bullet = GD.Load<PackedScene>("res://Bullet.tscn");
public override void _Input(InputEvent @event)
{
if (@event is InputEventMouseButton mouseButton)
{
if (mouseButton.ButtonIndex == MouseButton.Left && mouseButton.Pressed)
{
EmitSignal(SignalName.Shoot, _bullet, Rotation, Position);
}
}
}
public override void _Process(double delta)
{
LookAt(GetGlobalMousePosition());
}
}
在主場景中,我們連接遊戲角色的訊號(它將出現在“節點”分頁中)
func _on_player_shoot(Bullet, direction, location):
var spawned_bullet = Bullet.instantiate()
add_child(spawned_bullet)
spawned_bullet.rotation = direction
spawned_bullet.position = location
spawned_bullet.velocity = spawned_bullet.velocity.rotated(direction)
private void OnPlayerShoot(PackedScene bullet, float direction, Vector2 location)
{
var spawnedBullet = bullet.Instantiate<Bullet>();
AddChild(spawnedBullet);
spawnedBullet.Rotation = direction;
spawnedBullet.Position = location;
spawnedBullet.Velocity = spawnedBullet.Velocity.Rotated(direction);
}
現在子彈將自行運作,獨立於遊戲角色的旋轉: