1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Godot4で3D風2D縦スクロールシューティングゲームを作る 9回目 スコア処理と表示

Last updated at Posted at 2024-01-13

Godot4.1.3でシューティングゲームを作っていきます。

スコア処理と表示

 敵を撃墜したときにスコアを加算し、画面に表示します。

github

 本記事で実装したものをgithubで公開しています。

方針

 ゲーム全体を管理しているgame.gdスクリプトが、スコアを管理・表示することにします。
 撃墜判定は敵スクリプトのtwin_body.gdで実施しているため、敵スクリプトがgameスクリプトにスコアの加算を通知する必要があります。
 敵スクリプトはsignalでスコア更新通知を送信します。signalの接続先は、外部から設定してもらうようにして、敵スクリプトが外部に依存関係を持たせないようにします。

敵スクリプトで宣言したシグナルをgameに接続する

 シナリオ要素が敵を生成します。その時に接続先としてgameノードのメソッドを設定しますが、直接gameノードを呼び出すと依存関係が強くなりすぎるため、グローバル変数のノード経由で呼び出すようにします。
 そのため初期設定時に、gameノード自信をglobalに登録して、シナリオ要素は敵を生成したときに、globalに登録されたノードのメソッドをシグナルの接続先として、敵に設定します。

敵が撃墜された場合シグナルを発行

 敵スクリプトは撃墜されたことを判定すると、add_to_scoreシグナルを発行して、queue_freeにより消滅します。
 gameスクリプトがadd_to_scoreシグナルを受信し、スコアの処理を実行し、表示を更新します。

スコア表示領域を追加する

 game.tscnにスコア表示用のControlノードを追加します。
 ルートノード直下にControlを追加して、「HUD」に名称を変更します。
 HUDの子ノードとして、Labelを2つ追加して、名称を「score」と「score_value」に変更します。
スクリーンショット (878).png

2つのLabelはインスペクタを開き、それぞれ下記のように変更しました。

score : Label

「score」文字列を表示します。白の太字に設定します。

ノード名 変更箇所
Label/Text "score"
Label/新規LabelSettings/Font/Size 20px
Label/新規LabelSettings/Outline/Size 5px
Control/Size/x 61px
Control/Position/x 11px

score_value : Label

scoreの数字を表示します。黄色の太字に設定します。

ノード名 変更箇所
Label/Text "score"
Label/新規LabelSettings/Font/Size 20px
Label/新規LabelSettings/Font/Color 黄色 Hex:#c2c700
Label/新規LabelSettings/Outline/Size 5px
Label/新規LabelSettings/Outline/Color 黄色 Hex:#5eb000
Control/Size/x 118px
Control/Position/x 86px

撃墜された敵がadd_to_scoreシグナルを発行する

 twin_body.gdを開き下記のように修正します。

extends Area3D

+signal add_to_score(int)

@export var _d_speed_mps = 20.0
@export var _d_hp = 1
+@export var _i_score = 100

func _physics_process(delta):
	global_position += Vector3(0, 0, 1) * _d_speed_mps * delta
	if _d_hp <= 0:
+		add_to_score.emit(_i_score)
		queue_free()

func _on_visible_on_screen_notifier_3d_screen_exited():
	queue_free()

# HPからダメージポイントを引き算する。余った分はreturn値で返す
func calc_damage(laser_power) -> int:
	var remainder = 0
	if _d_hp <= laser_power:
		# ダメージの方が大きい
		remainder = laser_power - _d_hp
		_d_hp = 0
	else:
		# HPの方が大きい
		_d_hp -= laser_power
		remainder = 0
	# 余ったダメージポイントを返す
	return remainder

 発行するシグナルを宣言します。引数は1個でこの敵を倒した時のスコアを設定します。

+signal add_to_score(int)

 スコアはパラメータにします。

+@export var _i_score = 100

 HPが0になったときが撃墜された時なので、スコアを設定しadd_to_scoreシグナルを発行(emit)します。

func _physics_process(delta):
	global_position += Vector3(0, 0, 1) * _d_speed_mps * delta
	if _d_hp <= 0:
+		add_to_score.emit(_i_score)
		queue_free()

シグナルの接続先ノードをグローバル変数にする

 gameノードは、グローバル変数経由でアクセスするようにします。

extends Node3D

+# gameのインスタンス
+var node_game : Node3D = null 

# 生成したインスタンスの追加先
var node_lasers : Node3D = null
var node_enemies : Node3D = null 
var node_bullets : Node3D  = null

# Playerのインスタンス
var node_player : Area3D = null  

add_to_scoreを受けてスコアを表示する処理を実装する

 game.gdを開き、下記のように実装します。

extends Node3D

var _scn_scenario_stage = preload("res://scenario/scenario_stage01.tscn")

var _node_scenario_ins

+var _i_score : int = 0

func _ready():
+	# gameインスタンスを保存
+	g_val.node_game = self
	
	# インスタンス化したシーンの追加先を設定する
	g_val.node_lasers = $DynamicNodes/lasers
	g_val.node_enemies = $DynamicNodes/enemy/bodies
	g_val.node_bullets = $DynamicNodes/enemy/bullets

	# Playerインスタンスを設定する
	g_val.node_player = $Player
	
	# ステージシナリオをシーンツリーに追加
	_node_scenario_ins = _scn_scenario_stage.instantiate()
	add_child(_node_scenario_ins)

func _process(delta):
	# シナリオを駆動する
	_node_scenario_ins.drive_scenario()

+func _on_add_to_score(add_score):
+	_i_score += add_score
+	$HUD/score_value.text = "%010d" % _i_score 

 game.gdでスコアを管理するので、メンバ変数として_i_scoreを宣言します。

+var _i_score : int = 0

 add_to_scoreシグナルを受けて、スコアを加算し、score_valueラベルのtextを更新するメソッドを追加します。

+func _on_add_to_score(add_score):
+	_i_score += add_score
+	$HUD/score_value.text = "%010d" % _i_score 

 初期設定時に、gameノードを設定します。

func _ready():
+	# gameインスタンスを保存
+	g_val.node_game = $"."

敵インスタンスを生成したときに、add_to_socreシグナルの接続先を設定する

 シナリオ要素が、敵を生成したときに、add_to_scoreシグナルの接続先を設定します。
 produce_twin_body.gdを開き下記のように変更します。

extends Node

@export	var _i_produce_num = 10
@export var _d_produce_interval_sec = 0.5
var _scn_enemy = preload("res://character/enemy/twin_body.tscn")

var _i_produced_num = 0;	# 生産した数

func start_element():
	$Timer_for_produce.start(_d_produce_interval_sec)

func is_blocking_to_move_on_next_scenario() -> bool:
	# 敵キャラの生産が完了したら次のシナリオ要素に進ませるためfalseにする
	return ( _i_produced_num < _i_produce_num )

func _on_timer_for_produce_timeout():
	# 敵キャラクターのインスタンスを生成、位置を設定してシーンに追加する
	var ins = _scn_enemy.instantiate()
	ins.position.z = -200
	ins.position.x = randf_range(-25, 25)
+	# add_to_scoreシグナルをgame.gdに接続する
+	ins.add_to_score.connect( g_val.node_game._on_add_to_score )
	# 敵インスタンスをシーンツリーに追加
	g_val.node_enemies.add_child(ins)
	
	# 生産完了したら、タイマーを停止する
	_i_produced_num += 1
	if ( _i_produced_num >= _i_produce_num ):
		$Timer_for_produce. stop()

生成した敵インスタンスがinsです。insのadd_to_scoreシグナルを、グローバル変数を介してgameノードの_on_add_to_scoreメソッドに接続します。

+	# add_to_scoreシグナルをgame.gdに接続する
+	ins.add_to_score.connect( g_val.node_game._on_add_to_score )

実行結果

 敵を撃墜すると、スコアが加算されて表示が更新されることが確認できました。

image.png

以上です。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?