Up to date
This page is up to date for Godot 4.2
.
If you still find outdated information, please open an issue.
Instancing with signals¶
Signals provide a way to decouple game objects, allowing you to avoid forcing a
fixed arrangement of nodes. One sign that a signal might be called for is when
you find yourself using get_parent()
. Referring directly to a node's parent
means that you can't easily move that node to another location in the scene tree.
This can be especially problematic when you are instancing objects at runtime
and may want to place them in an arbitrary location in the running scene tree.
Below we'll consider an example of such a situation: firing bullets.
Przykład strzelanki¶
Consider a player character that can rotate and shoot towards the mouse. Every time the mouse button is clicked, we create an instance of the bullet at the player's location. See Tworzenie instancji for details.
We'll use an Area2D
for the bullet, which moves in a straight line at a
given velocity:
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;
}
}
Jednakże jeśli pociski zostaną dodane jako dzieci gracza to podczas jego obrotu pozostaną do niego "przyłączone":
Zamiast tego chcemy żeby pociski były niezależne od ruchu gracza - wystrzelone powinny lecieć w linii prostej i gracz nie powinien mieć już na nie żadnego wpływu. Zamiast być dodanym do drzewka sceny jako dziecko gracza stosowniejszym jest dodanie pocisku jako dziecka "main" sceny gry, która może być ojcem gracza albo być jeszcze wyżej w drzewku.
You could do this by adding the bullet to the main scene directly:
var bullet_instance = Bullet.instantiate()
get_parent().add_child(bullet_instance)
Node bulletInstance = Bullet.Instantiate();
GetParent().AddChild(bulletInstance);
However, this will lead to a different problem. Now if you try to test your "Player" scene independently, it will crash on shooting, because there is no parent node to access. This makes it a lot harder to test your player code independently and also means that if you decide to change your main scene's node structure, the player's parent may no longer be the appropriate node to receive the bullets.
Rozwiązaniem tego problemu jest użycie sygnału "emit" aby wyemitować pociski z gracza. Gracz w ten sposób nie musi "wiedzieć" co się od teraz stanie z pociskami - jakikolwiek węzeł, który jest podłączony do sygnału może przyjąć pociski i podjąć należyte kroki aby je utworzyć.
Oto kod dla gracza używającego sygnałów do wyemitowania pocisku:
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());
}
}
In the main scene, we then connect the player's signal (it will appear in the "Node" tab of the Inspector)
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);
}
Teraz pocisk będzie utrzymywał swój własny ruch, niezależnie od rotacji gracza: