4.衝突の準備
シグナルの追加
次におこなうのは衝突判定の準備です。Godot Engineの重要な要素となるシグナルをあつかいます。シグナルはノード間で情報をやり取りするためのルールです。
まず、スクリプトの先頭にシグナルを追加します。extends Area2Dの後にhitというシグナルを書きます。
extends Area2D signal hit
このhitは、「Player」が敵と衝突したときに「Player」が発信するシグナルにします。
「インスペクター」ドックがある右側サイドバーに「ノード」ドックがあるので開きます。
「ノード」ドックに、Playerが発信可能なシグナルの一覧が表示されます。今追加したhitも入っています。
シグナルの接続
シグナルは用意するだけでは駄目で、他のノードに接続する必要があります。接続する側では、_on_シグナル名関数を定義して、シグナルを受け取った際の処理を書きます。
ここでは、hitではなく、別のシグナルを受け取った時の処理を書きます。hitシグナルを受け取った時の処理は、他の場所で書きます。
「ノード」ドックに並ぶシグナル一覧から「Area2D」の「body_entered(body: Node2D)」を選んで右クリックします。そして「接続」を選びます。
「メソッドにシグナルを接続」ダイアログが表示されます。そのまま「接続」ボタンをクリックします。
player.gdに_on_body_entered関数が追加されます。また「ノード」ドックの「body_entered(body: Node2D)」の配下に「.::_on_body_entered()」が追加されます。
それぞれに緑色のアイコンが付いているのが分かると思います。緑のアイコンは、シグナルがこの関数に接続していることを示しています。
少し私見を書きます。シグナルの処理は直感的ではなく、自分が何をやっているのか自信がなくなり混乱しやすい部分です。混乱しないようにしてください。
シグナルを受け取るのが_on_~関数で、シグナルを発するのがシグナル.emit()です。そして、シグナルを受け取る際は、「ノード」ドックを使って接続します。
この接続の仕方はいろいろあり、他の接続方法(スプリクトから接続する方法)は今後の連載で出てきます。
_on_body_enteredの実装
それでは_on_body_entered関数の中身を書いていきましょう。
func _on_body_entered(body: Node2D) -> void:
hide() # 衝突したら、プレイヤーを隠す
hit.emit() # hitのシグナルを発信する
$CollisionShape2D.set_deferred("disabled", true) # フレーム最後に無効化する
最初の処理はhide関数です。敵と衝突したらプレイヤーを隠します。
次の処理はhit.emit関数です。hitのシグナルを発信します。どこかでhitに接続しているノードがあれば、ここで発信したシグナルを受信します。
最後の処理は、set_deferred関数を使い、「CollisionShape2D」ノードのdisabledをtrueに設定します。
set_deferred関数は、現在のフレームの最後にプロパティーを書き換えます。「CollisionShape2D」を無効化すれば、衝突が起きなくなります。
敵と衝突したら、このような手順でプレイヤーを隠して、シグナルを発信して、衝突処理を無効化します。
startの実装
最後にもう1つだけ関数を末尾に追加します。新しいゲームを始める時に、「Player」の状態をリセットするための関数です。この関数を外部から呼び出すことで、「Player」の状態をゲーム開始時の状態に戻します。
func start(pos: Vector2) -> void: position = pos # 初期位置を設定 show() # プレイヤーを表示 $CollisionShape2D.disabled = false # 無効化を解除
最初の処理では、引数のposをpositionに代入します。
次の処理はshow関数です。「Player」を表示します。
最後の処理は$CollisionShape2D.disabledの値をfalseにします。これで衝突範囲が有効に戻ります。ゲーム中とは違い、フレームの最後ではなく、即時に値を変更しています。
Playerの作業はこれで終了です。
ここまでのスクリプト
ここまでのスクリプトを掲載します。
extends Area2D
signal hit
@export var speed = 400 # プレイヤーが移動する速さ (pixels/sec)
var screen_size # ゲームウィンドウのサイズ
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
screen_size = get_viewport_rect().size
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
# キー入力による速度の設定
var velocity = Vector2.ZERO # Playreの移動ベクター
if Input.is_action_pressed("move_right"):
velocity.x += 1
if Input.is_action_pressed("move_left"):
velocity.x -= 1
if Input.is_action_pressed("move_down"):
velocity.y += 1
if Input.is_action_pressed("move_up"):
velocity.y -= 1
# 移動しているか、していなかで処理を分岐
if velocity.length() > 0:
# 移動しているなら、速度のベクトルを正規化したあと、係数を掛ける
velocity = velocity.normalized() * speed
$AnimatedSprite2D.play() # AnimatedSprite2Dを再生
else:
$AnimatedSprite2D.stop() # AnimatedSprite2Dを停止
# 位置を更新、画面外にはみ出さないようにする
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)
# 表示するアニメーションの種類と向きを設定
if velocity.x != 0:
$AnimatedSprite2D.animation = "walk"
$AnimatedSprite2D.flip_v = false # 縦方向の反転なし
$AnimatedSprite2D.flip_h = velocity.x < 0 # 横方向の反転
elif velocity.y != 0:
$AnimatedSprite2D.animation = "up"
$AnimatedSprite2D.flip_v = velocity.y > 0 # 縦方向の反転
func _on_body_entered(body: Node2D) -> void:
hide() # 衝突したら、プレイヤーを隠す
hit.emit() # hitのシグナルを発信する
$CollisionShape2D.set_deferred("disabled", true) # フレーム最後に無効化する
func start(pos) -> void:
position = pos # 初期位置を設定
show() # プレイヤーを表示
$CollisionShape2D.disabled = false # 無効化を解除
