Developer

さくさく理解する Godot 入門(ただし2Dに限る)応用編 レトロシューティングゲーム【第1回】
2021.12.09
Lv1

さくさく理解する Godot 入門(ただし2Dに限る)応用編 レトロシューティングゲーム【第1回】

目次

  • はじめに
  • 画面作成
  • メイン画面
  • 自機
  • 外部シーン
  • 衝突判定
  • 爆発イフェクト
  • 敵機タイマー

はじめに

前シリーズでは、動きのないパズルアプリの作成方法を示した。 本稿ではそれと打って変わり、動きが多く、アクション的なゲームアプリの作成方法を示す。
具体的には、下図のような見た目のレトロなシューティングゲームを作成する。

感のいい方はおわかりだと思うが「スペースインベーダー」もどきだ。
「スペースインベーダー」は1978年にリリースされ、日本・世界中を席巻したシューティングゲームの傑作だ。 衝突判定やリアルタイム処理をどのように実現すればいいかなど、アクションゲームの必須要素を多く含んでいるので、 Godot でゲームを作って見たい人へのとてもいい題材だと考えている。

※ 「スペースインベーダー」は株式会社タイトーの登録商標です。

例によって、ソースコードは github にて、 MIT ライセンスで公開している。

全ソースコードを解説すると膨大になってしまうので、本稿では、アクションゲーム特有の各フレーム処理、 (レイヤー・マスクを使った)衝突判定を重点的に解説する。 解説していない部分は比較的難易度が低い部分だと考えるので、詳細を知りたい方は自分でソースコードを読み解いてほしい。

また、筆者の要求に応じて本アプリ用の画像を新規に描いていただいた 西野竜平氏 には大変感謝している。 本当にありがとうございました。

画面作成

■メイン画面

プロジェクトを新規作成し、プロジェクト設定で画面サイズを 500×800 にする(その具体的な方法は前シリーズを参照)。

これまで何度か述べたように、Godot ではノードを配置して画面を作成し、それにスクリプトをアタッチして動きを作る、 という手順を踏む。というわけなので、以下画面作成について解説する。

画面背景として矩形(ColorRect)を設置し、画面サイズと同じサイズにする。

西野氏に描いてもらった外枠画像を TextureRect として設置。 ただし、このままでは、自機・敵機ミサイルが外枠画像より手前に描画されてしまうので、レイヤーを分ける。 そのために、CanvasLayer を設置し(ノード名は FrameLayer にリネームしている)、外枠画像はその子ノードとする。
また、下図のように、タイトル、スコア・ハイスコア、残基数、左右・発射ボタンもフレーム上に設置されるので、 それらもこのレイヤーの子ノードとしている。 下図にはUFO、自機(Fighter)も映っているが、これらは FrameLayer の子ノードではなく、ルートノードの子ノードになっている(左部のノードツリー参照)。

CanvasLayer の Layer は 2 としている(下図参照)。

Godot では基本ノードツリーの上から順番に画面描画されるので、下のものほど画面手前に描画されることになる。 したがって、後から追加されたノードは一番手前に表示されてしまうのだが、これが不都合な場合は、 このように CanvasLayer を用いるとよい。

■自機

自機は、衝突判定を行うので、物理ボディのひとつである KinematicBody2D とし、Sprite, CollisionShape2D を下図の様に子ノードとして持つ。 また、爆発時イフェクト用のパーティクルも子ノードとして持っておく。こうしておくと、 パーティクル発生位置をわざわざ設定しなくとも、自動的に自機と同じ位置になる。

■外部シーン

敵機、トーチカ、自機ミサイル、敵機ミサイルのように、動的に生成されて、その数が変動するものは、外部シーンとして作成しておき、 エディタで画面に配置するか、実行時にスクリプトで動的に画面に配置する。

敵機は衝突判定を行うので、下図のように KinematicBody2D をルートとし、Sprite、CollisionShape2D を子ノードとしておく。

敵機は下図のように、3種類*2パターンを横に並べたひとつの画像としておき、これを Sprite にアタッチする。

Sprite には下図のように Animation 用に、縦横フレーム数(Hframes, Vframes)、 どのフレームを表示するか(frame)のプロパティがある。 敵機のパターンは横に6並べたので、Hframes を6に設定しておく(下図参照)。

障害物であるトーチカ(バンカー)は、StaticBody2D をトーチカの形に並べて作成する。 下図のように、8×8 白矩形の Sprite と、同じサイズの CollisionShape2D から構成する。
矩形サイズを 8×8 でなく 4×4 や 2×2 にすれば、当然画面はきれいになるのだが、その分画面作成が大変になるので、筆者は 8×8 で妥協した。 根性のある人は 2×2 で作り直してみるのもいいかもしれない。

(子ノードも含めて)StaticBody2D をひとつ作ったら、あとはそれを複製(Ctrl + D)してトーチカの形に並べる。
このとき、スナップオプションをONにし、ツールバーの設定アイコン > スナップの設定 で、 下図のようにグリッドのステップを 8×8 に設定すると、StaticBody2D をバンカーの形に並べるのが容易になる。

自機・敵機ミサイルは移動するので StaticBody2D ではなく KinematicBody2D をルートノードにし、 Sprite と CollisionShape2D を子ノードとする(下図参照)。

■衝突判定

衝突判定を行うのは、自機ミサイル → 敵機、UFO、トーチカ、敵機ミサイル → 自機・トーチカ だ。 敵機ミサイルは敵機をすり抜ける。

これを可能にするため、それぞれの衝突判定レイヤーを分け、また衝突判定先のレイヤーをマスクとして指定する。

具体的には、自機はレイヤー1(左上)に、敵機・UFOはレイヤー3に、トーチカはレイヤー9(左下)に割り振っている(下図参照)。

そして下図が自機ミサイルと敵機ミサイルのレイヤーマスクの設定だ。

自機ミサイルは敵機、UFO、トーチカに衝突するので、マスク3と9を有効に、 敵機ミサイルは自機とトーチカに衝突するので、マスク1と9を有効にしている。

■爆発イフェクト

自機が敵ミサイルに当たってしまったときに表示する自機爆発パーティクル用として、 CPUParticle2D クラスを用いて爆発用シーン Explosion を作成する。 2Dパーティクルクラスには GPU を利用するものとCPUを利用するものの2種類が存在するが、 本アプリではweb版作成を前提とし、web版ではGPU利用のものは使用できないために、CPUParticle2D を利用することとした。
パーティクルの設定は下図のようにする。

パーティクルの各設定の概要を以下に示す。

Emitting(放出) パーティクル放出を ON/OFF できる。
(Time 直下の)ワンショットが ON の場合、パーティクルがすべて消えるとこのボタンが有効になる。 押下で再度パーティクルが生成される。
Amount(量) 生成されるパーティクルの数
Lifetime(生涯) 放出されたパーティクルの寿命(単位:秒)
Lifetime(生涯) 放出されたパーティクルの寿命(単位:秒)
One Shot(ワンショット) ON ならば、パーティクルを Amout 分放出したら終わり
Explosiveness(爆発性) 爆発的にパーティクルを生成
Texture パーティクルとして表示する画像。本アプリでは 4×4 の白矩形を用いている
Direction > Spread 広がる角度。180度に設定する。
Initial Velocity > Velocity Random
(初期速度 > ランダム)
最大値の1に設定しておく

動作を確認したい場合は、インスペクタ一番上の Emitting をONにするとよい。パーティクルが全部消えると、自動的にOFFになる。

下図に、エミットしたときの、パーティクルの様子を示す。

■敵機タイマー

敵機アニメーション、動き、ミサイル発射処理のために、下図のようにそれぞれのタイマーを用意しておく。

EnemyTimer は敵機の形を変えるアニメーション用タイマー、EnemyMoveTimer は敵機移動用タイマー、EnemyMissileTimer は敵機ミサイル発射のためのタイマーだ。

EnemyTimer、EnemyMoveTimer、EnemyMissileTimer の WaitTime は、それぞれ 1, 0.02, 1 と設定し、Autostart は全て ON にしておく。

それぞれ、timeout() シグナルをそれぞれのイベントハンドラに接続しておく。