快速エンジニア

駆け出しエンジニア、雑記を書く。Unity多め

Unityで音ゲーつくる 譜面読み込み機能

音ゲーを作りはじめてから2週間が過ぎ、宣言通り最低プレイできるレベルにはなりました。
一方で記事の執筆が全く進んでいないので備忘録としても書かないとですね。

 

ところでUdemyというサイトでUnityの講座を探していたら面白そうなのを見つけました。

www.udemy.com


かなりクオリティの高い2Dアクションゲームアプリが作れるみたいです。内容が良ければ学んだことなど受講レビュー的なのも書けるかなと思います。

 

さて、それでは譜面読み込み機能について書きたいと思います。
読み込むファイルは(.sus)という変わった形式ですね。中身についてはどうやらWikiがあったようで、もっと早く見つけていればよかった…。

sus形式 - sus-analyzer

「#TITLE xxx」「#ARTIST xxx」など#の直後が英字で始まるデータはメタデータ、「#00018: 00001800 」「#000280: 18000028」など数字で始まるのは譜面データだそうです。中身は1行ずつ書かれているのでFileクラスのReadAllLinesというメソッドが使えますね!

var lines = File.ReadAllLines(filePath, Encoding.UTF8);

foreach(var line in lines)
{
      // メタデータを読み込む処理
      // 譜面データを読み込む処理
}

肝心のデータを読み込む処理ですが、まず正規表現でパターンを用意しておいてそれにマッチする行のみに処理を適用させましょう。

private static string ScoreDataRegax = @"#([0-9]{3})([0-9A-Z]{2})(.*): (.*)";
private static List<string> MetaDataRegax = new List<string>
{
     @"#(TITLE) (.*)",
     @"#(ARTIST) (.*)",
     @"#(PLAYLEVEL) (.*)",
     @"#(WAVE) (.*)",
     @"#(WAVEOFFSET) (.*)",
     @"#(BPM01):(.*)"
};

if((Regex.Match(line, ScoreDataRegax)).Success){ // ここに処理を書く }

パターンに「( )」をつけておくとその部分をmatch.Groups[x+1番目]で取得できます。
メタデータは(TITLE, ARTIST)と(xの歌, yさん)など対応できるDictionaryに格納するといいと思います。
BPM01のデータだけはすぐ使うのでtempoなどの変数に入れておきましょう。

おさらいですが譜面データは以下のようになっています。
f:id:tantakotako:20210115235154p:plain

「:」の前後で分けたほうがいいです。後ろの部分はn分音符で分割されているので2桁ごとに区切って処理をします。
あとは上の画像のようにif文を使ってシングルノーツやロングノーツを生成させますが、結局それが何拍目のノーツなのかは計算する必要があります。
今回は4分の4拍子しか扱っていないため5小節目だった場合は5 * 4 + (i * 4 / (分割数))で求まります(iは区切った中のi番目という意味)。

後はシングルノーツの場合は今まで通りNotePropertyのインスタンスを生成して配列に代入させればokです。

noteProperties.Add(
     new NoteProperty(beat, beat, lane, NoteProperty.NoteType.Single)
);
noteProperty.secBegin = ScoreManager.ToSec(beat, tempo);
noteProperty.secEnd = ScoreManager.ToSec(beat, tempo);

ロングノーツは少し特殊で2桁ごとに区切ったデータが「18」の場合は始点、「28」の場合は終点となります。

 if(objNum[0] == '1')
{
     longNoteBegin = beat;
}
else if(objNum[0] == '2')
{
     noteProperties.Add(
         new NoteProperty(longNoteBegin, beat, lane, NoteProperty.NoteType.Long)
     );
}

このように十の位が1の時はノーツを生成しないで拍の情報だけ保持しておいて2の時に生成するようにします。