Godot4 ビッグカツブロック崩し22 ゲームの各タイミングで UI を表示

昔から人気の駄菓子「ビッグカツ」フリー素材画像が公開されたので、無料・軽快な 2D / 3D 用のゲームエンジン Godot Engine 4 を使って、ビッグカツ画像を使ったブロック崩しを作成します。

「ビッグカツブロック崩し」作成の第22回では、ゲームオーバーやゲームクリア、プレイ開始前やレベルクリア時のそれぞれのタイミングで表示するメッセージやボタンなどの UI を、それぞれのタイミングで表示を切り替えるためのスクリプトの例とそのテスト結果を紹介します。
ノードの表示・非表示を切り替える関数についても紹介します。

※ GodotEngine 4.3 を使用しています。.NET 版ではありません。
※スクリプトは自己責任でご使用ください。

前回の記事

前回は、ゲームオーバーやゲームクリアなどの様々なタイミングで表示するラベルやボタンの UI 群をステージのシーンに追加しました。

hide() と show() で各 UI の表示を切り替える

GUI コントロールの基本となる Control クラスの継承元 CanvasItem では、 show, hide メンバ関数を使って、そのコントロールの表示・非表示を切り替えられます。

Show the CanvasItem if it’s currently hidden. This is equivalent to setting visible to true. For controls that inherit Popup, the correct way to make them visible is to call one of the multiple popup*() functions instead.

CanvasItem が現在非表示になっている場合は表示します。これは、visible を true に設定するのと同じです。 Popup を継承するコントロールの場合、それらを表示する正しい方法は、代わりに複数の Popup*() 関数の 1 つを呼び出すことです。
CanvasItem — Godot Engine (4.x)の日本語のドキュメント #show と Google 翻訳

Hide the CanvasItem if it’s currently visible. This is equivalent to setting visible to false.

CanvasItem が現在表示されている場合は非表示にします。これは、visible を false に設定するのと同じです。
CanvasItem — Godot Engine (4.x)の日本語のドキュメント #hide と Google 翻訳

関数の説明で出てくる visible プロパティも CanvasItem のメンバです。

If true, this CanvasItem is drawn. The node is only visible if all of its ancestors are visible as well (in other words, is_visible_in_tree must return true).

Note: For controls that inherit Popup, the correct way to make them visible is to call one of the multiple popup*() functions instead.

true の場合、この CanvasItem が描画されます。ノードは、その祖先がすべ​​て表示されている場合にのみ表示されます (つまり、is_visible_in_tree は true を返す必要があります)。

注: Popup を継承するコントロールの場合、それらを表示する正しい方法は、代わりに複数の Popup*() 関数の 1 つを呼び出すことです。
CanvasItem — Godot Engine (4.x)の日本語のドキュメント #visible

スクリプト例

以下のスクリプトを作成済みの stage.gd に上書きすると、さきほど作成したゲームオーバー時の UI などが、そのタイミングに応じて UI が表示・非表示になります。
※ GameOverUI ノードなど各 UI の上位ノードの名前が異なる場合は、スクリプトでもその部分を置き換えてください。

前半部分は、以前に作成した、各面の表示、進行状況に応じたクリックへの反応、ブロックが全て消えた際のクリア判定などです。

今回新たに加わったのは、その後半の

  • on_level_ready
  • on_playing
  • on_level_clear
  • on_game_clear
  • on_game_over

関数で、それらが呼び出されると、その時表示する UI のノードを show 関数で表示したり、他の UI のノードを hide 関数で非表示にします。
また、現在のゲームの進行状況を管理する current_phase メンバ変数も変更します。
※詳細は、スクリプト内のコメントを参照してください。

extends Node2D
class_name Stage
## ゲーム進行を管理するクラスです。
## [member array_level_scene] へのレベルシーンの設定が必要です。

## ボールの初期位置です。
## $Ball の最初の位置を初期位置として保存して、各レベルの始まりにその位置にボールを配置します。
var ball_start_position: Vector2

## レベルごとに読み込むシーン (.tscn) を設定する配列です。1面から順番に設定してください。
## 
## 設定するシーンはレベルシーンと呼びます。
## レベルシーンは、 Node クラスのルートノードの下に block.tscn の Block の子ノードを1個以上配置したシーンです。
## ブロックはボールが届く範囲に配置してください。
## 例えば、以下のようなレベルシーンがあります。
## Level1(Node クラス)
##   |
##   +- Block1 (block.tscn)
##   |
##   +- Block2 (block.tscn)
##   |
##   +- Block3 (block.tscn)
@export var array_level_scene: Array[PackedScene]

## 現在のレベルです。[member array_level_scene] の要素番号として用います。
## 初期値は -1 ですが、 next_level 関数内で 1 増やされて 0 番目の [member array_level_scene] 要素が実体化します。
var current_level_index: int = -1

## 現在読み込んでいるレベルのシーンのインスタンス(子ノード)です。
var current_level_scene:Node = null

## 現在のゲームのプレイ段階の種類です。
## タイトルシーンと異なり、ゲームオーバーやゲームクリアはプレイ時の画面の上に UI を表記するので
## シーンを切り替えずに、ラベルやボタンなどを必要に応じて表示するため、同一のシーン内でフェーズを変えて
## 見た目や入力による反応を切り替えます。
enum Phase
{
	## レベルのプレイ開始直前のフェーズです。
	LevelReady,
	## レベルプレイ中のフェーズです。ボールを失うと GameOver に移行します。
	Playing,
	## レベルをクリアした直後のフェーズです。
	## 次の面がなければすぐに GameClear に移行します。
	## 次の面があれば、 Next ボタンをクリック後、次の面があれば StageStart に移行します。
	LevelClear,
	## ゲームオーバーのフェースです。タイトルボタンをクリックすると Title に移行します。
	GameOver,
	## ゲームクリアのフェーズです。タイトルボタンをクリックすると Title に移行します。
	GameClear,
}
## 現在のゲームの進行段階、フェーズを表します。
var current_phase = Phase.LevelReady

## Called when the node enters the scene tree for the first time.
## ノードが初めてシーン ツリーに入るときに呼び出されます。
func _ready():
	# プロパティの設定の確認
	if array_level_scene.size() < 1:
		printerr("Stage シーンの Stage ルートノードのプロパティ ArrayLevelScene にレベルシーンを1つ以上設定してください。")
	# ボールの初期位置を保存します。
	ball_start_position = $Ball.global_position
	# 1 面を読み込みます。
	current_level_index = -1 # next_level 関数内で +1 されて 0 になります。
	next_level() # 最初のレベルに進みます。
	return

## Called every frame. 'delta' is the elapsed time since the previous frame.
## フレームごとに呼び出されます。 delta は、前のフレームからの経過時間です。
## delta を使用しない場合は warning が出ないように _delta とリネームします。
func _process(_delta):
	# プレイ中
	if current_phase == Phase.Playing:
		# レベルシーンの持つ子ノード(ブロック)が全て消えたらレベルクリアに移ります。
		if current_level_scene.get_child_count() < 1:
			on_level_clear()
			
	# Enter / Space キー / マウス左ボタン(クリック) が押された直後の場合
	if Input.is_action_just_pressed("ui_accept") or Input.is_action_just_pressed("ui_click"):
	#if Input.is_action_just_pressed("ui_accept") or Input.is_action_just_pressed("ui_right") or Input.is_action_just_pressed("ui_left"):
		# ゲームの進行状況によって、入力があった際の挙動を変えます。
		match current_phase:
			Phase.LevelReady:
				# プレイ前の状態で入力されたら、ボールを動かして、プレイを開始します。
				on_playing()
			Phase.LevelClear:
				# 現在のレベルをクリアした状態で入力があった場合は、次のレベルに進みます。
				next_level()
			Phase.GameOver:
				# ゲームオーバー時は、 Retry ボタンまたは Title ボタンで
				# 遷移先を決めるので、画面クリックでは何もしません。
				pass
			Phase.GameClear:
				# ゲームクリア状態で、入力があればタイトルに戻ります。
				get_tree().change_scene_to_file("res://title_scene.tscn")
			_:
				# 上記以外
				printerr("想定していない Phase の列挙子の値です。")
				# TODO: Ball が下に落ちた場合の処理を追加

## 次のレベルに進みます。レベルシーンを実体化して、Phase.StageReady の状態になります。
## 次のレベルシーンがない場合は、ゲームクリアの処理に移ります。
func next_level():
	# 次のレベルの要素番号に更新します。
	current_level_index += 1
	# [member array_level_scene] の要素数(面の数)を超えて、次のレベルがない場合
	if array_level_scene.size() < (current_level_index + 1):
		# ゲームクリア
		on_game_clear()
	else:
		# 直前のレベルシーンを解放します。
		if current_level_scene != null:
			current_level_scene.queue_free()
		# 次の面のレベルシーンをインスタンス化します。
		current_level_scene = array_level_scene[current_level_index].instantiate()
		# Stage ルートノードの子ノードとしてレベルシーンのインスタンスを追加します。
		add_child(current_level_scene)
		# ボールを初期位置に配置します。
		$Ball.global_position = ball_start_position
		# ゲームの進行状況を StageReady にします。
		on_level_ready()
		return

## プレイ開始直前に呼び出されます。
func on_level_ready():
	# ゲームの進行状況を更新します。
	current_phase = Phase.LevelReady
	print("current_phase = Phase.LevelReady")
	# ゲームオーバーとクリア時の UI を非表示にします。下位のノードも非表示になります。
	# 非表示のボタンはその場所をクリックしても反応しません。
	$GameOverUI.hide()
	$GameClearUI.hide()
	$LevelClearUI.hide()
	# Ready UI を有効にします。
	$LevelReadyUI.show()

## 現在のレベルのプレイ開始時に呼び出されます。
func on_playing():
	# ゲームの進行状況を更新します。
	current_phase = Phase.Playing
	print("current_phase = Phase.Playing")
	# Ready UI を無効にします。
	$LevelReadyUI.hide()
	# ボールの一時停止を解除します。
	$Ball.is_pause = false
	return

## 現在のレベルをクリアした際に呼び出されます。
func on_level_clear():
	# ゲームの進行状況を更新します。
	current_phase = Phase.LevelClear
	print("current_phase = Phase.LevelClear")
	# レベルクリアの UI を有効にします。
	$LevelClearUI.show()
	# ボールを一時停止します。
	$Ball.is_pause = true
	return

## 全てのレベルをクリアしてゲームをクリアした際に呼び出されます。
func on_game_clear():
	# ゲームの進行状況を更新します。
	current_phase = Phase.GameClear
	print("current_phase = Phase.GameClear")
	# レベルクリアの UI を無効にします。
	$LevelClearUI.hide()
	# ゲームクリアの UI を有効にします。
	$GameClearUI.show()
	# ボールを一時停止します。
	$Ball.is_pause = true
	return

## ゲームオーバーになった際に呼び出されます。
func on_game_over():
	# ゲームの進行状況を更新します。
	current_phase = Phase.GameOver
	print("current_phase = Phase.GameOver")	
	# ゲームオーバーの UI を有効にします。
	$GameOverUI.show()
	# ボールを一時停止します。
	$Ball.is_pause = true
	return

## Ball ノードが画面外に出た際に発する
## カスタムノイズ ball_screen_exited を受信するメソッドです。
func _on_ball_ball_screen_exited():
	# ゲームオーバー時の処理を呼び出します。
	on_game_over()
	return

テスト

F6 キーで、開いているステージのシーンを実行します。

  • LevelReady
  • Playing
  • LevelClear

の順番で、フェーズが進み、 LevelReady と LevelClear ではメッセージが表示されました。

すべての面(レベル)をクリアすると GameClear フェーズに移り、メッセージが表示され、クリックするとタイトルシーンに戻りました。

ボールが画面下に落下すると GameOver フェーズに移り、メッセージリトライギブアップボタンが表示されました。

まとめ

「ビッグカツブロック崩し」作成の第22回では、ゲームオーバーやゲームクリア、プレイ開始前やレベルクリア時のそれぞれのタイミングで表示するメッセージやボタンなどの UI を、それぞれのタイミングで表示を切り替えるためのスクリプトの例とそのテスト結果を紹介します。
ノードの表示・非表示を切り替える関数についても紹介しました。

参照サイト Thank You!

記事一覧 → Compota-Soft-Press

コメント

Ads Blocker Image Powered by Code Help Pro

お願い - Ads Blocker Detected

このサイトは広告を掲載して運営しています。

ポップアップを閉じて閲覧できますが、よろしければ

このサイト内の広告を非表示にする拡張機能をオフにしていただけませんか?

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

We have detected that you are using extensions to block ads. Please support us by disabling these ads blocker.

タイトルとURLをコピーしました