はじめてのGodot 第7回: スコアを表示しよう — UIの基本


前回でゲームオーバーができました。今回は生存時間スコアを表示します。「よけゲー」がついに「スコアを競うゲーム」になります。

今回のゴール

プレイ中に上部のスコアがカウントアップし、ゲームオーバー時に中央へ最終スコアが表示される様子

  • 画面上部に生存時間がリアルタイム表示される
  • ゲームオーバー時に最終スコアが表示される

UIは「別のレイヤー」に置く — CanvasLayer

スコアやゲームオーバー表示のようなUI(画面表示)は、宇宙船や隕石のいる「ゲーム世界」とは別の場所に置くのが作法です。そのためのノードが CanvasLayer です。

CanvasLayerは「画面に直接貼り付くガラス板」だと思ってください。今作のカメラは動きませんが、たとえばマップをスクロールするゲームでは、ゲーム世界に直接置いたスコアは画面の外に流れて行ってしまいます。ガラス板に貼っておけば、世界がどれだけ動いてもスコアは画面の同じ場所に居続けます。今のうちに正しい作法で作っておきましょう。

HUDを組む

main.tscn で次のように組みます(HUD = ゲーム中の画面表示の通称です)。

  1. Main の子に CanvasLayer を追加し、名前を HUD
  2. HUD の子に Label を追加し、名前を ScoreLabel に。画面の上部中央に配置し、フォントサイズを32くらいに
  3. 前回作った GameOverLabel をドラッグして HUD の子に移動する
Main
├── Player
├── SpawnTimer
└── HUD (CanvasLayer)
    ├── ScoreLabel
    └── GameOverLabel

%固有名 — 階層が変わっても壊れない参照

ここで困ったことがひとつ。GameOverLabel を移動したので、前回書いた $GameOverLabel という参照は壊れました(実行するとエラーになります)。$HUD/GameOverLabel に直してもいいのですが、また移動したらまた壊れます。

もっと良い方法があります。シーンドックで ScoreLabelGameOverLabel をそれぞれ右クリックし、「固有名でアクセス」を選んでください。ノード名の横に % マークが付きます。こうしたノードは、どこに移動してもスクリプトから %名前 で参照できます

スコアを実装する

main.gd を次のようにします。

extends Node2D

const METEOR_SCENE = preload("res://meteor.tscn")

var score = 0.0
var playing = true

func _ready():
	$SpawnTimer.timeout.connect(_on_spawn_timer_timeout)

func _process(delta):
	if playing:
		score += delta
		%ScoreLabel.text = "SCORE: %.1f" % score

func _on_spawn_timer_timeout():
	var meteor = METEOR_SCENE.instantiate()
	meteor.position = Vector2(randf_range(20, 460), -50)
	meteor.speed = randf_range(200, 500)
	add_child(meteor)

func _on_player_died():
	playing = false
	$SpawnTimer.stop()
	%ScoreLabel.hide()
	%GameOverLabel.text = "GAME OVER\nSCORE: %.1f" % score
	%GameOverLabel.show()

実行してみてください。スコアが増えていき、当たると最終スコア付きでGAME OVERが出れば成功です。なお、ゲームオーバー時は最終スコアを中央にまとめて見せるので、上部の ScoreLabel%ScoreLabel.hide() で隠しています(同じスコアが2か所に出るのを防ぐため)。

新しいことを整理

生存時間 = deltaの積み立て

		score += delta

delta は「前のフレームからの経過時間」でした。毎フレーム足し込めば、合計が経過秒数そのものになります。第2回で学んだdeltaの、見事な再利用です。

playing フラグ — 状態を変数で持つ

var playing = true は「ゲーム進行中か?」を覚えておく変数です(こういう真/偽の変数をフラグと呼びます)。死んだら false にして、if playing: でスコア加算を止めています。「今ゲームはどういう状態か」を変数で持つ — これはどんなゲームにも出てくる基本パターンです。

文字列と % フォーマット

		%ScoreLabel.text = "SCORE: %.1f" % score

"..." で囲んだものが文字列(文字のデータ)です。Labelの text に文字列を入れると画面に表示されます。

"%.1f" % score は「scoreを小数1桁の文字にして埋め込む」という書き方です。score12.3456789... のような細かい数なので、そのまま表示すると桁が暴れて読めません。%.1f12.3 に整えています。

\n は改行を意味する特別な文字です。

日本語を表示したい場合

Textに日本語を入れると、デフォルトフォントでも一応表示されますが、ゲームの雰囲気に合うフォントを使いたくなったら、フォントファイル(.ttf)をプロジェクトにドラッグ&ドロップして、LabelのTheme Overrides→Fontsに設定します。Google Fontsに商用可の日本語フォントがたくさんあります。

つまずきポイント

  • Node not found: "GameOverLabel": HUDに移動したのに $GameOverLabel のままになっている箇所が残っています。%GameOverLabel に置き換えてください(固有名の設定も忘れずに)
  • スコアの数字がすごい桁で表示される: %.1f の書き忘れです
  • スコアが画面に出ない: ScoreLabelが画面外にいないか、HUDの子になっているか確認してください

今回学んだこと

  • UIは CanvasLayer(画面に貼り付くガラス板)に置く
  • %固有名 を使うと、ノードを移動しても参照が壊れない
  • delta を毎フレーム足すと経過時間になる
  • 状態は playing のようなフラグ変数で持つ
  • 文字列 "..."% フォーマットで数値を表示用に整える

次回予告

いよいよ最終回。音とリスタートを付けて、何度でも遊べる「完成品」に仕上げます。

第8回(最終回): 音とリスタートで「ゲーム」に仕上げよう