シグナルによるインスタンス化¶
シグナルは、ゲームオブジェクトを分離する方法を提供し、ノードの固定配置を強制することを回避できます。シグナルが必要となる兆候の1つは、get_parent()
を使用して自分自身を見つけたときです。ノードの親を直接参照するということは、そのノードをシーンツリー内の別の場所に簡単に移動できないことを意味します。これは、実行時にオブジェクトをインスタンス化し、実行中のシーンツリーの任意の場所に配置する場合に特に問題になる可能性があります。
以下に、そのような状況の例を考えます: 弾丸を発射します。
発射処理の例¶
マウスに向かって回転して弾を撃つことができるプレイヤーキャラクターを考えてみましょう。マウスボタンがクリックされるたびに、プレイヤーの場所に弾丸のインスタンスが作成されます。詳細については、インスタンスの作成 を参照してください。
弾丸には Area2D
を使用します。これは、指定された速度で直線的に移動します:
extends Area2D
var velocity = Vector2.ZERO
func _physics_process(delta):
position += velocity * delta
public class Bullet : Area2D
{
Vector2 Velocity = new Vector2();
public override void _PhysicsProcess(float delta)
{
Position += Velocity * delta;
}
}
ただし、弾丸がプレイヤーの子として追加された場合、プレイヤーが回転したときに、弾丸も「アタッチ」されたままになります:
代わりに、弾丸はプレイヤーの動きから独立している必要があります。発射されたら、弾丸は直線で移動し続ける必要があり、プレイヤーは弾丸に影響を与えることができなくなります。プレイヤーの子としてシーンツリーに追加する代わりに、弾丸をプレイヤーの親またはツリーのさらに上位にある「メイン」ゲームシーンの子として追加する方が理にかなっています。
これを行うには、弾丸をメインシーンに直接追加します:
var bullet_instance = Bullet.instance()
get_parent().add_child(bullet_instance)
Node bulletInstance = Bullet.Instance();
GetParent().AddChild(bulletInstance);
ただし、これは別の問題につながります。 「プレイヤー」シーンを個別にテストしようとすると、(弾丸の追加先として)アクセスする親ノードがないため、確認時にクラッシュします。これにより、プレイヤーコードを個別にテストすることが非常に難しくなります。また、メインシーンのノード構造を変更する場合、プレイヤーの親が弾丸を受け取る適切なノードではなくなる可能性があります。
これに対する解決策は、プレイヤーから弾丸を「放出」するシグナルを使用することです。プレイヤーはその後、弾丸に何が起こるかを「知る」必要はありません - シグナルに接続されているノードは、弾丸を「受信」し、弾丸を発生させるために適切なアクションを実行できます。
シグナルを使用して弾丸を発射するプレイヤーのコードは次のとおりです:
extends Sprite
signal shoot(bullet, direction, location)
var Bullet = preload("res://Bullet.tscn")
func _input(event):
if event is InputEventMouseButton:
if event.button_index == BUTTON_LEFT and event.pressed:
emit_signal("shoot", Bullet, rotation, position)
func _process(delta):
look_at(get_global_mouse_position())
public class Player : Sprite
{
[Signal]
delegate void Shoot(PackedScene bullet, Vector2 direction, Vector2 location);
private PackedScene _bullet = GD.Load<PackedScene>("res://Bullet.tscn");
public override void _Input(InputEvent event)
{
if (input is InputEventMouseButton mouseButton)
{
if (mouseButton.ButtonIndex == (int)ButtonList.Left && mouseButton.Pressed)
{
EmitSignal(nameof(Shoot), _bullet, Rotation, Position);
}
}
}
public override _Process(float delta)
{
LookAt(GetGlobalMousePosition());
}
}
メインシーンで、プレイヤーのシグナルを接続します([ノード]タブに表示されます)。
func _on_Player_shoot(Bullet, direction, location):
var b = Bullet.instance()
add_child(b)
b.rotation = direction
b.position = location
b.velocity = b.velocity.rotated(direction)
public void _on_Player_Shoot(PackedScene bullet, Vector2 direction, Vector2 location)
{
var bulletInstance = (Bullet)bullet.Instance();
AddChild(bulletInstance);
bulletInstance.Rotation = direction;
bulletInstance.Position = location;
bulletInstance.Velocity = bulletInstance.Velocity.Rotated(direction);
}
これで、弾丸はプレイヤーの回転に関係なく独自の動きを維持します: