【Godot】PONGゲームを作る その1

GodotでPONGというゲームを作るための解説記事です。PONGを作ってどうするんだという感じですが、Godotは初めてなので簡単そうなものから作って見ます。Godotのバージョンは4系。

ちなみにPONG(ポン)というのは卓球をテーマにしたビデオゲームです。

プロジェクトの作成

Godotを起動して新規プロジェクトの作成を行います。プロジェクト名とプロジェクトパスを指定して、「作成して編集」ボタンを押します。

プロジェクト設定

プロジェクトを作成したら、最初に設定をしておきます。

画面サイズ

メニューの「プロジェクト」⇒「プロジェクト設定」を開き、表示タブのウィンドウで画面サイズを任意の値にします。今回は640×480に設定します。

画面サイズの変更不可

画面サイズは変更できないようにオフにしておきます。

ピクセルパーフェクト

画像を拡大したときにぼやけるのでピクセルパーフェクトを設定。Unityのときはアセットインストールして、色々設定しないといけない記憶があったけど、簡単に設定できて良いですね。

プロジェクトのフォルダ構成

プロジェクトのフォルダ構成に決まりはありませんが、自分は以下のようにしています。特に決まりは無いみたいなので作りやすいように構成すればいいと思います。

画像をプロジェクトに追加

ゲームで使用する画像をプロジェクトに追加。そのままでは使用できないので設定をします。

ボール画像

Sprite2Dノードに画像を追加。Hframes, Vframesで縦横の分割数を指定して、Frameで表示する画像を選択します。

パドル画像

パドルの画像もボールと同様にプロジェクト内に取り込みます。

アニメーション設定

ボールが落下したときのアニメーションを設定します。

AnimatedSprite2D

BallシーンにAnimatedSprite2Dを追加。

インスペクターで新規SpriteFramesを作成。

作成したSpriteFramesをクリックするとアニメーションフレーム画面が開きます。スプライトシートからフレームを追加するをクリックして、アニメーション画像を選択します。

H,Vに分割

縦、横のサイズを指定、すべて選択をクリックしてフレームを追加します。

アニメーションを1回のみ再生

アニメーションを1回だけ再生したいときは、赤枠のアニメーションループをOFFにする必要があります。このアニメーションループがONの状態ではアニメーション完了のシグナルが発生しないので注意が必要です。

ノードとシーン

Godot では複数のノードで構成されたものをシーンと呼び、そのシーンを組み合わせてゲームを作るみたいです。シーンはUnityではprefabに相当。シーンはタイトル画面、ゲーム画面の各画面であったりプレイヤー、エネミーなどゲームオブジェクトもシーンにできます。

Mainシーン

Mainシーンでゲーム全体を管理します。今回はMainシーンに子ノードとしてBallシーン、Paddleシーンを持たせます。

ノード構成

Mainシーンのノード構成は以下です。

  • BGLayer: 背景表示用のレイヤー
  • MainLayer: ボールとパドルのレイヤー
  • UILayer: UI用のレイヤー

背景の描画順

背景やUIの描画順を制御するためにCanvasLayerを使用しました。インスペクタから「 Layer」 ⇒ Layer の値を変更することにより描画順を制御できます。この値は小さいほど画面の奥に描画されます。

ゲーム遷移

Mainシーンにゲームの状態(eState)を定義して、GDScriptのmatch文により分岐することによりゲームを更新します。match文は他の言語のswitch文と同等です。

ゲーム開始時にINITで初期化して、READYからMAINへとゲームの状態が遷移していきます。ボールを落下させてしまうと相手の得点になり、一定の得点に到達するとGAMEOVERとなります。

Main.gd
## 状態.
enum eState {
  INIT,
  READY, # 開始.
  MAIN, # メイン.
  GAMEOVER, # ゲームオーバー.
  GAMECLEAR, # ゲームクリア.
}
	
func _process(delta):
  match _state:
    eState.INIT:
      update_init()
    eState.READY:
      update_ready(delta)
    eState.MAIN:
      update_main(delta)
    eState.GAMEOVER:
      update_game_over(delta)

ボール(Ball)オブジェクト

ボールの移動処理

ボールは落下しない限り、_process()内でpositionを更新していきます。

func _process(delta):
	if is_dead():
		return
	position.x+=_velocity.x
	position.y+=_velocity.y

ボールの落下判定

ボールの落下判定は一定の位置を超えると落下とみなします。

func _process(delta):
	if is_dead():
		return
	position.x+=_velocity.x
	position.y+=_velocity.y
	
	if position.x > _screen_size.x-10:
		# playerのミス
		dead(true)
	elif position.x < 10:
		# enemyのミス
		dead(false)
	# 壁の当たり判定
	if position.y > _screen_size.y-20 or position.y < 60:
		_velocity.y = -_velocity.y
		$AudioWall.play()

パドル(Paddle)オブジェクト

自分のパドルはキー入力により操作しますが、相手プレイヤーのパドルは自動で動くようにします。

キー入力

キー入力の処理を追加するには以下の方法が必要です。

メニューの「プロジェクト」⇒「プロジェクト設定」のインプットマップを選択。新しいアクションの追加に名前を入力して追加します。

インプットマップで設定した名前(”up_move”)をis_action_pressed()に渡します。毎フレーム判定したいので_process()関数内で実行しています。

func _process(delta: float):
  if Input.is_action_pressed("up_move"):
    # 何かしらの処理  

パドルの移動(慣性あり)

移動処理はpositionに直接値を入れています。キー入力を離した瞬間に停止すると違和感があるので、徐々にスピードを落として停止するようにしています。

func _process(delta: float):
  if Input.is_action_pressed("up_move"):
    _btnpress = true
    _speed = -0.11
  if Input.is_action_pressed("down_move"):
    _btnpress = true
    _speed = 0.11
  if not _btnpress:
    _speed /= 1.002

  position.y += float(_speed)

パドルとボールの跳ね返り処理

パドルとボールが衝突したときは移動方向を単純に反転するだけにしています。パドルの当たる位置によって移動方向を調節できるようにするべきですが、とりあえずはこれでOKとします。

パドルの識別

MainLayerに自分と相手の二つのパドルオブジェクトを配置しています。パドルを識別するために@export キーワードを使用しました。

スクリプトで@export キーワードを使用すると、以下の画像のようにインスペクターで変数を変えることができます。

BGM, SEの再生

音を鳴らすとゲームの感じが出るので鳴らしてみました。BallシーンにAudioStreamPlayer2Dを追加。再生するSEを指定します。

Ballシーンのスクリプトで以下のコードを記述してSEを再生することができます。この方法だとSEの数だけAudioStreamPlayer2Dが必要になるので、AudioManagerなどのクラスを作成して、IDを指定して鳴らすみたいなことが必要そうです。今回はめんどくさいのでやっていません。

# ノード名.play()で再生できる
$AudioDamage.play()

実行ファイルの出力

最後にexe形式で作成したゲームを出力します。メニューの「プロジェクト」⇒「エクスポート」を選択。エクスポートの画面が開くので「追加」でWindows Desktopを選択します。

色々設定できますが、デフォルトで大丈夫です。プロジェクトのエクスポートをクリックします。

ファイル保存ダイアログが開くので、保存先を指定します。注意点として「デバッグ付きエクスポート」にチェックが入っているので外します。これにチェックが入っているとゲームのタイトル名に(DEBUG)の文字が付いてしまいます。

保存先のexeファイルを実行できればエクスポートは完了です。

Godotの感想

Godotに触ってみましたが、以下の点が良いと感じました。

  • 動作が軽い、起動が早い
  • GDScriptにより早く試作することができる
  • 比較的自由に作ることができる

動作が軽い、起動が早い

Godotは2Dゲーム用に特化しており余計な機能が無いので、軽くて起動も早いです。個人的に起動が早いということを非常に重要視しています。ゲームを作りたいと思ったときに起動が遅いとモチベが下がることがあるからです。動作が軽いので低スペックノートPCでも快適に開発することができます。

GDScriptにより早く試作することができる

GodotではGDScriptと呼ばれるPythonに似ている専用の言語のスクリプトが使えます。C#も使用できるみたいですが、公式はGDScriptを推奨しているみたいです。

初めは専用の言語か…という拒否感がありましたが、慣れてくると短いコードで素早く書けるので良い感じです。

比較的自由に作ることができる

感覚的な話ですが、Unityだとワークフローが決まっていてなんか作りにくいと感じるケースがあります。また自分は2Dのゲームしか作らないのでUnityのような大規模エンジンをなんとなく好きになれない。Godotは我流でひとりでゲームを作る人に向いている気がします。

終わりに

PONGゲームの基本的な動きまで実装しました。当たり判定のめり込みやボールとパドルの跳ね返りが一定など問題点が多く残っています。またゲームとしては見た目が地味でつまらないので、次回はその部分を改善していきます。