どれくらい正確に主旋律を取得できているか確認するため、弾の登録部分に短い効果音を入れてみました。
※限定公開動画ですが、削除した方がよければ削除します。
完璧かと言うとまだ改善の余地がありそうですね。
ただ、10年前私が大学院の頃であれば、音楽から完璧に主旋律を取り出すことができれば学会で発表できるレベルです。
想像ですが、パスフィルターを使って人間の声の周波数領域のみを取り出し、ボイスからフーリエ変換を使うと
よりボーカルの声のリズムに従った旋律の取得ができるかもしれません。
でもそうなると、前奏や間奏の時どうなるのかといった問題が生じそうです・・。
この辺は工夫のし甲斐がありそうですね。
私も音楽に合わせた弾幕を過去に作ったことがあります。
http://www.nicovideo.jp/watch/sm4655464
http://www.nicovideo.jp/watch/sm4687447
※「神曲と弾幕をシンクロさせてみた」を見てわかる通り、効果音なしに音楽と同調していることを意識させるには、かなり弾速を早くするか、レーザーのように一瞬で光が届くように見せるなどの工夫をしてみたらどうでしょう。
しかしこれはバカみたいな実装方法で、事前に自分で曲に合わせてエンターキーをたたき、
その時たたいたのと同じリズムで弾を発射させることで実現しています。
当然ユーザーが取り込んだ曲に自動的に適用することは不可能になります。
好きな曲で弾幕が遊べるというコンセプトだと思うので、これとは少し違いますね。
> 私のC言語、C++言語の知識は全て龍神録プログラミングの館から学びました。
> 他の本や指南サイトを辞書的に読むことはあっても、他の書き方を知らないのでどうしても似通ってしまいます。
お恥ずかしや・・。
私はゲームプログラミングを専門としていないので、参考にするのはそこそこにちゃんとした書籍で勉強した方がよいかと思います。
私の専門はモバイルアプリ開発なので・・ゲーム製作は完全な素人が好きで作ってるレベルです。
そして龍神録2プログラミングの館が途中になっているので困ったことでしょう。。。。
子供がある程度育ってきて子供が起きている時間にPCがいじれなくなってしまったのです。。。
また時間を見つけて制作したいと思います。
> TDD(テスト駆動開発)は文章を書くときの下書きのようなものだと理解しました。
テスト駆動開発はその名の通り、テストコードを書いて実行しながら作っていく開発スタイルです。
もしテストコードを書いたことがなければ、一度簡単なテストコードを書いてみるとよいでしょう。
> ポリモーフィズム
私が話しているポリモーフィズムの内容と少し違うことを仰っているかもしれません。
ストラテジーパターンによる実装を勉強してみるとわかり易いかと思います。
https://ja.wikipedia.org/wiki/Strategy_ ... C%E3%83%B3
龍神録にでてきそうな例で雑魚とボスのクラスで説明してみます。
コード:
#include <iostream>
#include <list>
using namespace std;
class IBaseEnemy {
public:
virtual ~IBaseEnemy() = default;
virtual void draw() = 0;
};
class Zako : public IBaseEnemy {
public:
void draw() override {
cout << "Zako" << endl;
}
};
class Boss : public IBaseEnemy {
public:
void draw() override {
cout << "Boss" << endl;
}
};
int main() {
list<IBaseEnemy*> enemyList;
enemyList.emplace_back(new Zako());
enemyList.emplace_back(new Boss());
enemyList.emplace_back(new Boss());
enemyList.emplace_back(new Zako());
enemyList.emplace_back(new Zako());
for (auto p : enemyList) {
p->draw();
}
}
インスタンスの所有者はZakoなのかBossなのか意識しません。
全てに一斉にdrawやupdateといった処理を指示すればよくすることで所有者の負担が軽減されます。
ポリモーフィズムはオブジェクト指向の醍醐味です。
ゲームプログラミングの館でも紹介しています。
https://dixq.net/g/sp_06.html
> 当たり判定
当たり判定って結構どのように設計するか実は難しいんですよね。
しかし、基本的にオブジェクト指向においてよく言われるのは「Tell, Don't Ask」です。
要するに「get関数を使って値を取得するな。必要なあることはそのクラスにお願いしろ」ということです。
簡単な例をコードで示します。
今、「誕生日」のクラスがあったとしましょう。
そして、誕生日の「10日後」を表示するコードを2種類紹介します。
コード:
#include <iostream>
using namespace std;
class Birthday {
int mDay;
public:
Birthday(int day) : mDay(day) {
}
int get() {
return mDay;
}
int add(int day) {
mDay += day;
return mDay;
}
};
int main() {
{//悪い例
Birthday birthday(20);
int after10days = birthday.get() + 11;
cout << after10days << endl;
}
{//良い例
Birthday birthday(20);
int after10days = birthday.add(11);
cout << after10days << endl;
}
}
どちらも「31」と表示されると思います。
最初、「20」が設定されていて、その11日後を表示させたいとき、
悪い例の実装方法だと、ロジックがインスタンスの所有者に依存します。
11日後を計算するというロジックが隠蔽されていないので、所有者の負担が大きいのです。
月の20日の11日後は31日という計算結果ですが、
4月や6月は30日までしかありませんから、その時は1にならなければなりません。
そのうえ2月なら28日しかありませんし、うるう年を考えるなら29日も考慮に入れる必要があります。
このような計算をしようと思ったらロジックがどんどん肥大化します。
このロジックをインスタンスの所有者の責務にするとどんどん負担が増えてしまうのです。
オブジェクト指向ではデータとロジックを同じクラスに実装します。
こういう話は私が先ほど紹介した本にも書いてあるのでじっくり読んでみてください。
> ご紹介頂きました書籍も、近日中には図書館にでも探しに行って読んでみます。
良いプログラミングの本って図書館にあるんでしょうか・・。
この辺の本は常に新しい本を読んだ方がいいので、大きな書店に行って端から読んで気に入ったものを買って読むのが良いかと思います。
もし金銭的な都合があるようであれば、キーワードさえわかればウェブでも学べます。
例えば「デザインパターン」とかでぐぐっただけでも、先人が体系化したよい設計のお手本が学べます。