DXライブラリで作る音ゲー
この記事はCCS Advent Calendar 2017 16日目の記事です。
前の人 まっそうめん
前回も音ゲーについて書きました。ただ、情報量不足過ぎた気がするので、再度書き直しです。
音ゲーって奥深いですよね。初めて作ったときすごく思いました。
ちょっと昔のお話を。
初めて音ゲーを作ったとき、それは2年生の頃です。
大学の授業でJavaを触ることがありました。その最終課題が「キーボードやマウスで何かしら動かせるものを作る」というものでした。今思うと流石に自由すぎるだろ…。
そう思いながら当時私はボルテやら弐寺やらたくさん音ゲーを触ってきて楽しくなってきた頃でしたので真っ先に「じゃあ音ゲー作るか」と発言してしまった。
何も考えず発言したのが間違いだった。
まずJavaの基礎ができてないのに音ゲーを作るなんて難易度が高すぎた。
最終的にできた音ゲーはこちら。
QUINTUPLE BEATSである。
説明的な何か↓
ゲーム本体↓
ただ欠点が多すぎた
・低スペックPCだとまともに動かない(ちゃんと音同期もできているが、ノーツがガッタガタ)
・機能が少ない(AUTOとかミラーとかハードゲージとか作りたかった)
・コードがくっそ汚い(ほんと人には見せられない。メイン5000行。アホ。)
などなど、上げるとキリがない。
そしてその後このゲームを何を血迷ったか
DXライブラリに移植した。
まあめんどくさいめんどくさい。
そのゲームがこちらである↓(ゲーム本体リードミー同封)
QUINTUPLE BEATS.zip - Google ドライブ
機能と譜面が増えた。中身はほとんど一緒である!
(CCS作品集2017夏 夏コミ)
その後全く違う音ゲーを作り始める。
MUSIC=OPERATORである。
体験版を置いておいたので是非プレーして欲しい。
それではメインの音ゲー制作について語っていこう。
※注意
ある程度プログラミングでゲームを作ったことが無いと内容がよくわからないかもしれません。ご了承ください。
実はコード上でやっていることは割と単純で、
・譜面を読み込む
・読み込んだ譜面データはノーツごとに配列で管理
・読み込んだ譜面をノーツごとに降ってくる時間を計算
・ノーツが特定の時間になった時にボタンを押していたら判定処理を行う
これだけである。基本はこの4つ。これにスコア機能とかなんかまあ色々つけると大変なことになるんだなこれが。
・譜面を読み込む
譜面以外の曲名や難易度やBPMについてはコード上に記載、スコアやクリア状態はセーブデータに記載する。
譜面データについては、一列目にBPMとズレを記載、2列目以降にノーツの記載
[小節数、カウント数(拍数みたいなもの。最大値16)、ノーツのレーン、ノーツの種類(単押しorロング)、ロングなら長さ(拍数で長さを決める)]
ノーツの一番最後にノーツの種類を9にすることで譜面終了判定を行っている。
なぜこんなことをしているかというと、曲の終わり≠譜面の終了とは限らないためである。
・読み込んだ譜面データはノーツごとに配列で管理
クラスでも構造体でもなんでもいいが、ノーツの降ってくる時間・種類・レーン・そのレーン上で何番目のノーツか・ロングノーツの時間・x座標y座標
を記載できるものを作って、上記で読み込んだ譜面をノーツごとに配列にぶちこんでいく。
・読み込んだ譜面をノーツごとに降ってくる時間を計算
降ってくる時間=14400÷BPM×((小節数-1) + (カウント数-1)÷16)
で計算できる。
まずなぜ赤字の14400になるかという説明をしよう。
1秒間で60フレームなら1分=60秒なら3600フレームだ。
ここでBPMについて。
BPMとは60秒間で降ってくる拍数のことである。←すごく重要
つまり、BPMが120なら、60秒間で120拍存在することになる。
1小節が4/4拍子だった場合、1小節に4拍存在する。
よって、60秒(3600)÷BPM*4=14400÷BPMで小節ごとのノーツの降ってくる時間を計算できるのである。
ただし、ゲームは必ずしも60フレームで動いているとは限らない。
というか基本的に60フレーム固定にはならない。
そのために、PCの実時間を取得する必要がある。
「言語名 PC時間取得」とかでぐぐれば出るはずである。これは書く言語によって変わると思う。
これでPC時間を取得して、1秒間を固定60フレームにしてあげる必要がある。
例えばC言語Windowsの場合、Windowsの時間取得を用いる際は
GetNowHiPerformanceCount();
もしくは
GetNowCount();
を利用して時間を取得できる。
そして、このカウント関数の型はLONGLONGだったりまちまちなので、変数を使う際は必ず型を気をつけて記載していただきたい。
doubleでたりるっしょとか思ってたらすぐにVSに怒られるぞ!
あとは現在時刻とノーツ落下時刻の差を取ってあげて、それにハイスピードをかけてあげれば音ゲーの基本システムが完成する。
NowTime…現在時刻
Notes[i].Time...ノーツごとの落下時刻
PlayerHiSpeed..ノーツ落下速度スピード(ユーザーが変更可能)
Notes[i].y...ノーツのy座標
とすると、
Notes[i].y=(Notes[i].Time - NowTime) * PlayerHiSpeed
と計算することで現在ある座標が計算できる。
判定については、譜面読み込みの段階で説明した「そのレーンで何番目のノーツか」が重要になってくる。
例えば速い縦連を想像してもらいたい。
速い縦連の場合、同じレーンに判定時間がかぶって存在していることになる。
その場合、1度押しただけで複数ノーツが判定されてしまう。これではそのあとのノーツ全てMISSになってしまいゲームにならない。
ここで、何番目のノーツかが役に立つのである。
判定ラインにかぶった際に、現在処理できるノーツがそのノーツかの判定を行えば、多重判定をされなくて済むのである。
この実装は結構苦戦するかもしれないので頑張って欲しい。
だが、基本的には「判定をしたら次のノーツへ判定移行」をするだけなので、ゲームのコードが書ける人ならできるはずである。
長々と話してきたが、基本は以上である。
これらを応用すれば、DIVAとかの難しい処理もできるようになるはずである。
わからないことがあったら@starealnightに直接話しかけてもらえばアドバイスをもらえるかもしれない。
こまつな (@starealnight) | Twitter
そして音ゲー制作に興味があるのならば音ゲー制作サークル「Rhythnibus」に是非入っていただきたい。
Rhythnibusに入りたい場合も@starealnightにリプライDMをもらえればOKである。
以上、文字だらけでごめんね!
自作音ゲーやってね!