大福未来研究所

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

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

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

(あくまでも音ゲーとしての実装は自己流のものですので、
実際はもっと効率のいい組み方があったりすることもあると思いますが、
そういう場合は是非ブログ等でその技術をシェアしていただけるととても助かりますので
何卒よろしくお願いします……)


*第4回、いよいよ譜面を叩く時。
というわけで前回の続きです。
前回の記事では、譜面が流れてくるところまでが実現できました。
今回は、それに入力に対応し、判定を取れるようにしましょう。

叩く基準、それが判定ライン

まずは判定ラインを作ります。これはあくまでも見せかけのためのものですから、
適当なキューブを伸ばしてライン上にすれば良いかと。
f:id:dfk_ohnuma:20171208005723p:plain

これで判定ラインが出来ました。
あっさりすぎるだろ。
あと、全てのシーケンスラインBPのX座標を、判定ラインの線に合わせて下さい。その位置を基準にして譜面は配置されていきますので、
ここに合わせていないとジャストのタイミングで判定ラインと重ならなくなります。

情報を登録していくスタイル

というわけで、次に判定用の処理を考えましょう。
シーケンスラインに並べられたオブジェは、原則として一番手前側にあるものから処理されていきます。
配列で言えばその時の0番目ですね。
入力があった際、比べる先は配列の0番目、そして内容は、
「入力があった時点の楽曲開始からの経過時間」と「該当オブジェに登録された時間」を比較すれば良いわけです。
それではAddNoteイベントを拡張してみましょう。
f:id:dfk_ohnuma:20171208010432p:plain
譜面を配置すると同時に、NoteArrayという配列型変数へ、
オブジェへのリファレンス、オブジェの登録時間、オブジェのタイプをそれぞれ突っ込みます。
オブジェのタイプについては、今はまだロングノートの事を考えないようにするために、一切気にしないでおいてください。

これで、譜面が配置されると同時に譜面が配列へ突っ込まれるようになりました。
このNoteArrayはNoteInfoStructという構造体変数を配列にしたものです
f:id:dfk_ohnuma:20171208011908p:plain
これで、シーケンスラインごとにオブジェを配列にして管理することが出来るようになりました。

また、楽曲の開始前にすこしオフセットを取りたい場合(ゲームメイン画面に入って2秒ぐらいしてから開始したい、とかそういう場合)
f:id:dfk_ohnuma:20171208020327p:plain
こうやって、配置場所にオフセットを足してやって下さい。


登録した情報は使ってなんぼ

というわけで、入力された際にNoteArrayの一番上のオブジェの情報を参照することで、
入力に対して判定を取ることが出来ますね。
それでは早速、そのためのマクロを作ります。
現在の時間と、NoteArrayの一番上の要素の時間とを比較し、差し引きした値を判定に使うために返します。
f:id:dfk_ohnuma:20171208012321p:plain
これの絶対値が小さいほど、正確なタイミングで入力されたということになります。
ここで戻り値を絶対値にしないのは、見逃しMISSを判定できるようにするためです。

これを使って、さっそく入力用の処理を作りましょう。
f:id:dfk_ohnuma:20171208012513p:plain
判定用の変数はPerfect、Great、Good、Bad、Missと5種類ありましたが、
Great判定を採用するのを辞めたのでこうなってます。
判定の数や基準とする判定タイム幅については自由に決めてみて下さい。

見逃したらダメ、ゼッタイ。

更に見逃しMISSを実装しましょう。
シーケンスラインのTickの最後尾に、見逃しミス用の判定を付け足します。
f:id:dfk_ohnuma:20171208014728p:plain
先程のマクロを絶対値ではなく、見逃したときだけ判定が成立するように使っているのがわかりますね。

起承転結

さてこれで判定は対応できましたが、ここで大事なものを付け足さないといけません。
それは「終わり」です。
曲がいつ終わるのかを情報としてゲーム側へ渡せないと、
いつまでもゲームが続いてしまいます。


なので、まずは譜面に終了用のコマンド「END」を追加しましょう。
f:id:dfk_ohnuma:20171208015010p:plain
コマンドEND:引数1…到達したら終了する小節数


そして、このコマンドをSequenceloaderが読めるようにしてあげます。
f:id:dfk_ohnuma:20171208015404p:plain
そこから伸びるピンはここへつなぎます。
f:id:dfk_ohnuma:20171208015835p:plain
あとは終了時間を超えていないかチェックすることで、
自動的に楽曲終了→リザルトへの移行が出来るようになります。
f:id:dfk_ohnuma:20171208015930p:plain


レベルはこうやってタイトル・セレクト・ゲームメイン・リザルトの4つを作っておけば、
簡潔なゲームであれば最低限のゲームとしての遷移は出来ると思います。
f:id:dfk_ohnuma:20171208020049p:plain

ついでにちゃんと動いているかどうか確認するためにウィジェットも作りましょう。
f:id:dfk_ohnuma:20171208021920p:plain
こうやってテキストブロックを用意して、まず最初にGameInstanceのリファレンスを用意して
f:id:dfk_ohnuma:20171208022034p:plain
それから、それぞれのテキストブロックの内容について関数をバインドさせてやればよいでしょう。
f:id:dfk_ohnuma:20171208022140p:plain

とりあえず、ここまでの状態で譜面を流してみて、
入力をちゃんと取れるかを確認してみましょう。

t.co
まだ爆発などは用意してませんが、タイミングよく入力すればオブジェが消えてくれるはずです。
次回はゲームとしてタイトルからリザルトまでを回せるように整えていきます。

最後に

これで入力に対して判定を取ることが出来るようになりました。
この時点ですでに、音ゲーとしての遊び方を用意することは出来ました。音は鳴ってないけど。
次回はタイトル画面や選曲画面やリザルト画面をとりあえず作って、ゲームとしての体裁を整えていきます。

以上で今回の記事は終わりです。お疲れ様でした。
アドカレの翌日の記事も私です(というか25日まで全部私です)。よろしくお願いします。