大福未来研究所

大福フューチャーラボのなんか色々なアレ。プログラミング(C++/DirectX/Unity/UE4あたりやりたい)とか音楽とかの。

【UE4】UE4でシンプルな音ゲーを作ろう:その1

この記事は、「ひとりUE4 Advent Calendar 2017」の12/3担当記事です。
qiita.com

音楽ゲームって難しいんでしょ?

最初にこの記事を開いた時、そう思われた方は多いのではないでしょうか。
んなこたーない
色々とめんどくさいですが、それでも作るのがそこまで難しいわけではありません。
ゲームエンジンを使って作った場合、音を鳴らすまでがラクラクでできるので、
むしろとっとと作ってしまうことができます。
だってほら、
togetter.com
こんなの作ってたわけですし。
大丈夫、いけるいける!!
というわけで、この「土日で音ゲー作ろう」をやったときの流れを、
一週間に分けて解説します。
自己流で色々汚かったり力技だったりする部分もありますが、どうかお付き合いしていただけると嬉しいです。
今日は第一回です。


今回の音楽ゲームは、極力シンプルな作りにするために、
以下のような仕様にしてあります。
・6レーン
・1曲1譜面
・キー音はない
・曲のテンポは最初から最後まで一定とする

第1回、それは音ゲーの譜面の仕組みを考える時。

最初にするべきことは何でしょうか。
まずはどんな見た目の音ゲーにするかを考えるときですよね。
私は6レーンの奥から手前にオブジェが飛んでくるタイプ、つまり王道of王道音ゲーにすることを選びました。

ではその次は?
メイン画面を作るべきでしょうか。それとも……?


私はまず、譜面の仕組みを作ることにしました。

音ゲーの譜面って実際どうなのさ

まず音ゲーの譜面を作る側と、動かす側で考えてみたいと思います。


動かす側、つまりゲームシステム側からすれば、
譜面オブジェ(以降オブジェと言います)に必要なのは
・どの時間に叩くものか
・どのレーンか
が必要です。


逆に、譜面を作る側からすれば、
オブジェを置く際にx.xx秒とかy.yyy秒だなんてめんどくさい書き方をすると、
頭がこんがらがってきてしまいます。
ですから、譜面を作る側にとって必要な情報は
・テンポがいくつの、何小節目の何分の何拍目か
・どのレーンか
ということにすればよいのです。

そして、その橋渡しをするために譜面データを作り、それを読み取る仕組みを作れば良いのです。

構造体を作ろう→データテーブルも作ろう

というわけで、まずは譜面データの核になる構造体を作ります。
譜面データは譜面の位置だけが書かれていても意味がありません。
曲の名前や、テンポや、wavファイルによっては開始地点からどれだけずらすかなども記録しておく必要があります。
というわけでこのような構造体を作りました。
f:id:dfk_ohnuma:20171203232428p:plain
3つの文字列変数。「コマンド」「引数1」「引数2」です。
コマンドによってその行の役割を設定し、引数1と引数2はコマンドごとの内容になります。
この構造体を元にして、譜面のデータテーブルを作ります。
f:id:dfk_ohnuma:20171203232653p:plain
今回の譜面データには、以下のようなコマンドが使えるようにしたいと思います。


TITLEコマンド:引数1で曲名を、引数2でアーティスト名を表記する
GENREコマンド:引数1でジャンル名を表記する 引数2はどうでもいい
BPMコマンド:引数1でテンポを表記する 引数2はどうでもいい
(数値)コマンド:(数値)小節の(引数1)レーン目に(引数2)の通りにオブジェを配置する

この、譜面配置コマンドにおける引数2の文字列について説明します。

譜面配置の文字列の意味

これは、いわゆるBMS形式と呼ばれる音楽ゲームの譜面フォーマットに似たものになっていますが、

・文字列のうち、0が休符、1が音符。
・文字列の文字数で1小節を分割して、0のところには何も置かずに1のところにだけ音符を置く。

というルールです。
例えば「1」だけだと小節の頭にオブジェが一つ設置されます。
「1111」だとオブジェが4分音符間隔で設置されます。
「11111」だと、オブジェが5分音符(めったに使わなそう)間隔で設置されます。
「1011」だと、「1111」の2つ目の音符がなくなります。

わかってきたでしょうか?
この方式を使うことで、複数の拍子が混ざりあうような複雑なタイミングでも、細かいタイミングでも、
その小節に入る譜面の分母の最小公倍数だけ文字列を長くすることで、表現することができます。

それでは、この譜面テーブルを解読するためのBPを作ります。

譜面を読み込む仕組みを作ろう

というわけで、
f:id:dfk_ohnuma:20171203232752p:plain
まあまず、こんな感じにしました。
譜面データの全部の行をチェックし、その列を分解して、最初のコマンド部分に入っている文字列でスイッチします。
この時、曲の名前やアーティスト名、ジャンル名、テンポ数値を何処かに表示させたいこともあるかもしれないので、
それらの値を保存しておこうと思います。GameInstanceを使いましょう。
f:id:dfk_ohnuma:20171203234038p:plain
なんとまあ乱暴な変数名だ


そしてこのGameInstanceをゲームに設定しましょう。
f:id:dfk_ohnuma:20171203234149p:plain
こんな感じで設定。


そしてその中に変数を用意しておきます。
f:id:dfk_ohnuma:20171203234223p:plain
上から、
曲名、アーティスト名、ジャンル名、テンポ、パーフェクト数、グレート数、グッド数、バッド数、ミス数、最大コンボ、そして得点です。
で、そこに色々保存していきます。
f:id:dfk_ohnuma:20171203233905p:plain
こんな感じ。ジャンル名、曲名、アーティスト名、テンポは保存できるようになりましたね。

更に、
f:id:dfk_ohnuma:20171203234427p:plain
「Make Note」なる関数を作りました。
中身は……おっと今日はここまで!!


今回は序章なのでここまでです

ということで結構スピーディに譜面を読み込む仕組みを作ってましたが、
ちょっとあっという間過ぎたでしょうか?もし速すぎてもうちょっと細かく説明を……ということがあれば、
私のツイッター等へご連絡下さいませ。


というわけで、今日の記事はここまでです。お疲れ様でした。
明日は第二回、譜面配置のシステムづくりです。