このチュートリアルでは、弾幕システムのサンプルゲームであるBulletHellSampleの構成や仕組みについて解説を行います。
◆ゲーム概要
HP500の弾幕を放つボスをショットやボムで倒すゲームです。
サンプルなので倒すまでで終わりです。
矢印キーで移動、Zキーでショット、Xキーでボムです。
弾幕で良く使われる「かすり」や「ボムによる弾消し」などを実装しています。
ゲームシーン構成
ゲームシーンはtest_scene.tscnのみでシーン遷移はありません。
オブジェクト構成
プレイヤー機
player.tscnがプレイヤー機のシーンです。
子ノードとしては、かすりを実現するためのオブジェクトGrazeObject.tscnと被弾判定を行うためのオブジェクト、player_hit_checker.tscnがあります。
エネミー機
enemy.tscnがエネミー機のシーンです。
子ノードとしては、被弾判定を行うためのオブジェクト、damage_checker.tscnがあります。
各機能の実現
エネミー機の弾幕
シンプルに一定時間ごとに「弾幕を発射」するという作りで実装しています。
1つ目の弾幕は加速する全方位弾
2つ目の弾幕は移動を組み合わせたショットガン
3つ目の弾幕はステージ端からのプレイヤー狙い分裂弾
4つ目の弾幕は全方位団を組み合わせた花模様
5つ目の弾幕は方向転換弾を使った簡単な図形描写
です。
弾幕では、SEを繰り返し再生する必要があるため、アニメーションプレイヤーノードでAudioStreamPlayerの音を鳴らすことで実装しています。
適切な音源がないためこの実装方式としていますが、弾幕毎に適切な音源を作成し「音を再生」する方が本来は良いです。
*現状の弾幕システムでは停止手段がないため、敵を倒しても生成処理に入った弾幕は停止しません。これはアップデートで弾幕停止手段を追加することで修正予定です。現時点では、リピート処理を使わずステートを分けてリピート処理を作ることで回避することができます。
プレイヤー機の通常ショット弾幕
こちらは4種類の弾幕を1つのステートで同時に発射するという形で実装しています。現時点では速射タイプでは方向を指定して発射することができないため、「非常に遠くのグローバル座標に向けて発射する」ことで擬似的に方向を再現しています。
*近い内に方向指定で発射できるようにアップデート予定です。
かすり処理
プレイヤーのあたり判定より大きな当たり判定を持つオブジェクトGrazeObject.tscnを子ノードとして配置して実現しています。そのオブジェクトが「弾幕とあたる」とかすり用のパーティクルを放ちスコアを追加します。
被ダメージ処理
被ダメージの処理はプレイヤー、エネミー共に子ノードとした別オブジェクトを使っています。
理由としては大きく2点です。
- 被ダメージによって実行中のステートが中断されることを避けるため。
- 「かすり」と「被ダメージ」のHIT判定を明確に分けるため。
*ACTION GAME MAKERの「HitArea2D」の判定はルートノード以下全てのHitAreaと接触判定をチェックするため、子ノードのHit判定も拾ってしまいます。この問題は「子ノードではなく子オブジェクトとして生成」することでも回避できますが、あたり判定の正確な位置を目視しやすいようにするため今回の方式としました。
弾幕のレイヤー構成
プレイヤーはレイヤー1、エネミーはレイヤー2、かすり判定はレイヤー3として構築しています。
敵の弾幕のレイヤーは1,3
プレイヤーの弾幕のレイヤーは2
です。
ボムの実装
ボムは弾幕ではなく、通常の弾として実装しています。
bomb.tscnがボムのシーンです。
画面全体にあたり判定を設定し、当たり判定と命中した弾幕を消し、10%の確率でスコアアイテムに変換するように設定しています。
また、敵本体にもダメージを与えるように攻撃判定も設定しています。
*スコアアイテムへの変換はかなりおもい処理であるオブジェクト生成を行うため、100%等に設定すると処理落ちが発生します。この問題への対策は別途検討中です。
スコアアイテム
オブジェクト生成の処理負荷を軽減するため、GDScriptで作成しています。
シーンはscore_test.tscnです。
プレイヤーに近づき、一定距離になると消滅して指定の変数を増減します。
Scoreノードのインスペクターで数値をカスタマイズをすることができます。
PickUpRadius: 接触判定距離
PullSpeed: 速度
Group: 近づいていく対象グループ
Project Variable: 変更するプロジェクト変数名
Variable Add: 加算値