はじめに
私が制作したゲームTriVerseの内部実装を基に音ゲーのノーツ生成や判定の実装について説明していきます。予定では、基本実装編、ノーツ生成編、判定編の3本構成で紹介していこうと思います。今回の基本実装編では、ノーツオブジェクトの作成やレーンの作成を紹介します。
この記事の対象者
- 音ゲーを作りたい人
- チョットだけUnityが分かる人
TriVerseについて
せっかくなので制作した音ゲーについて説明させてください。TriVerseでは以下のプレイ画面のように3つのレーンからなる3つのグループあり、それらを切り替えながら奏でていく音楽ゲームです。A, S, Dをノーツに合わせてタイミング良く押します。←, →でグループを切り替えます。今回の画像だと白くなっていない所ですね。
良ければ是非実際に遊んでみてください!

ノーツの設計
TriVerseではノーツが3つあります。
| ノーツ種類 | 操作内容 |
|---|---|
| タップノーツ | レーンに合わせて単押し |
| ホールドノーツ | レーンに合わせて長押し |
| フリックノーツ | 矢印に合わせて ← → を押す |
もちろん、それぞれに対応するクラスを個別に作ることもできますが、ノーツには共通している処理がいくつもありますよね?
例えば、「生成後、上から降ってくる」「判定ラインを通過したら削除する」などはすべてのノーツに共通する動きです。
そこで、共通部分をベースクラスとしてまとめ、各ノーツ種別はそこから継承する形にしています。簡易的に表すと次のようになります。
ここで音ゲーに詳しい方は、「ノーツにレーン情報を持たせていないけれど大丈夫なの?」と思ったかもしれません。
詳しくは判定編で説明しますが、TriVerseではノーツ判定にイベント駆動型の仕組みを採用しています。そのため、ノーツのGameObject自体にレーン情報を直接持たせる必要がなく、判定で参照される情報は判定装置のイベントデータ側で管理されています。
つまり、先ほど作ったノーツは主に“表示用”として機能し、判定は別のデータ構造に基づいて行われる、というわけです。
また、ノーツ新たに生成して削除を繰り返すととても重くなってしまうのでUnityのObjectPoolを使用しています。判定ラインを過ぎたら非表示、ノーツ生成時に初期化し表示、これによりGC負荷を軽減しています。
ノーツジェネレータ
TriVerseでは、ひとつのノーツマネージャーと9レーンそれぞれのノーツジェネレータで構成されています。
詳しくはノーツ生成編で説明しますがおおまかな流れとしては以下のようになります。かなり簡略化しているので実際の動作とは少し異なります。
責務の分離をするために、マネージャーは主に譜面からレーンと生成時間を管理して、ジェネレータはレーン単位でそれぞれノーツの実体を生成しています。
また、ObjectPoolを使用しているので同じPoolを指定する必要があり、Poolの初期化をマネージャー側で行いジェネレータに渡すことにより全レーンで統一しています。
ゲーム管理
ゲーム管理に関してはよくあるGameManegarを作成し、そこから他のオブジェクトに指示を出すという方式を取っています。
Start関数だったらまだいいのですが、Update関数を多用すると可読性が落ちると思っているので、Updateが必要な場合はGameManegarから呼び出すようにしています。ただし、Notesに関しては例外的にUpdateを使っています。
TriVerseではノーツは表示用としてのみ動作するので、GameManagerから呼び出すメリットがあまりありません。処理する順番もあんまり関係ないのでね
ゲーム進行を担うクラスについて紹介します
開始、終了、ページ遷移や設定の反映をしているインゲームのコア部分です
こちらの処理を見ればおおまかなゲーム中の処理の流れを把握することができるようにしています
-
NotesManager
上のノーツジェネレータで説明した通りです
-
JudgeSystem
名前の通り判定を司っているクラスです
詳しくは判定編で説明します -
ScoreManager
スコアの計算や判定の集計などを司っています
-
GameSettings
上の3つとは少し毛色が違いますが、楽曲情報やユーザー設定を保持しています
-
UIや判定エフェクト関連
UIControllersフォルダにコンボや判定描画などが入っています
このように責務をしっかりと分離しておくことでバグ修正が楽になります。
さいごに
TriVerseでの設計思想をまとめてみました。これから音ゲーを作りたいという人の設計の手助けになれば幸いです。間違いやこちらの方が良いなどのご意見がありましたら、気軽にコメントをお願いします。
次回は、ノーツ生成に関しての記事になります。