添削をお願い致します

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
鈴狼
記事: 2
登録日時: 5年前

添削をお願い致します

#1

投稿記事 by 鈴狼 » 5年前

おはようございます、いつもお世話になっております。
以前こちらでご助言を頂き、無事に一つの弾幕STGを完成させることができました。
念願の同人イベントへのサークル参加も叶いました。改めて、御礼申し上げます。

今回作りました作品は、ユーザーが用意した好きな音楽から自動生成される弾幕を攻略するSTGです。
音声波形を利用して、音が高まるタイミングを検知して弾を生成する仕組みになっております。

(以下、youtubeに投稿した参考動画です)


今後は、今回の作品を改良して次回作に繋げて行きたい考えです。
つきましては、より完成度を高める為に全体の添削をお願いしたく存じます。
より良い処理や設計の方法がありましたら、ご教授頂ければと思います。

開発環境は、[OS:windows7][コンパイラ:VisualStudio2017][ライブラリ:DXライブラリ]を使用しています。

よろしくお願い致します。
添付ファイル
鈴狼の添削依頼.zip
有料頒布しているので体験版verで失礼致します。Dxlibも入れてません。
(40.54 MiB) ダウンロード数: 159 回

アバター
Dixq (管理人)
管理人
記事: 1661
登録日時: 13年前
住所: 北海道札幌市
連絡を取る:

Re: 添削をお願い致します

#2

投稿記事 by Dixq (管理人) » 5年前

おぉ、すごいですね。
どうやって音声データを解析しているのかと思いきや、
今のDXライブラリにはGetFFTVibrationSoftSoundなる関数があるのですね。
しばらくDXライブラリから離れていてすっかり浦島です。
私もAerobeatという音ゲーを作ったとき、ユーザーが取り込んだ音楽から自動的に譜面データを生成できないか試行錯誤したものでした。
フーリエ変換を使って試行錯誤したものの、結局実用レベルには到達しませんでしたが・・。

動画を拝見しましたが、音楽にどの程度マッチして弾幕が出せているのか明確には分かりませんでした。
もし明確にマッチできているのであれば、弾のショット音を付けると気持ちいのではないでしょうか。
(龍神録2プログラミングの館で使用している効果音使っていただいて大丈夫ですよ。)

また、音楽に合わせた弾幕にゆっくりした精密系の濃い弾幕は合わない気がします。
というのも、弾幕が濃すぎるとプレイヤーの場所しか目がいかず、
肝心な音に合わせて弾が出ている部分が見えないと思うからです。

速いショットで、音楽に合わせて弾が出ている様子が遊んでいてわかるような弾幕がいいのではないでしょうか。

次にコーディングについてですが、大枠は龍神録2プログラミングの館に似ているのですね。
ただ、気になったのがprivateメンバ変数としてstatic変数があることです。
幸い私が見た範囲では外部クラスから参照していないようなのですが、なぜstaticになっているのでしょう?
singletonパターン等以外で、メンバ変数をstaticにすることは一般的にアンチパターンです。

メンバ変数の命名規則に一貫性がないようです。
C++では慣習的に
int mHogeHoge;
のようにかくことが多いです。
(なお、私がよく書いてしまう_から始まるメンバ変数はJavaのなごりです)

関数名にも命名の改善の余地がありそうです(揺らぎも大変多いようです)。
calc();
といった関数は設計者ではない人が見た時にそれをコールすることで何が行われるか予想できないです。
コードは物語のように読めるようにかくことが望ましいとされています。
例えば

コード:

if(container.isEmpty()){
    register(container);
}
こんなコードを英語として読んだとき、そのまま日本語にすると
「もし入れ物が空だったらコンテナを登録する」と読めます。こんな感じです。

また、テストコードを書いてコードのテストを実行したいと思った時、
「そのコードはTestableか?」を意識するとよい設計になります。
私はゲーム製作が好きなわりには、GUIアプリケーション開発の専門なのですが、
GUIアプリケーションの設計アーキテクチャにはMVPやMVVMなどといった
モデル部・ビュー部・プレゼンター部などを分けて役割分担させる方法があります。

見える部分にかかわらず計算が必要なモデル部分はゲームプログラムにもあります。
それらを切り離してTestableな設計にすることで綺麗な設計が期待できます。
https://www.google.com/search?q=TDD&ie= ... refox-b-ab
この辺を見てみるとよいでしょう。

せっかくTaskクラスを継承しているのにポリモーフィズムを利用していないところが多いのでオブジェクト指向のメリットがいかされていません。

また、Out(enemyshot_and_playerやplayershot_and_enemy)があるあたりオブジェクト指向の設計になっていません。

オブジェクト指向でなぜつくるのか


現場で役立つシステム設計の原則

オブジェクト指向をきちんと使いたいあなたへ

一度この辺の本を読んでみてください。きっと設計の考え方が変わると思います。
中でも上の本の増田さんという方は私が個人的に好きな著者で、オブジェクト指向をわかり易く教えてくれます。

・・・と遅い時間になってしまったので今日はこれで。またお話しましょう。

※追記
registerはC++では予約語でしたね・・。最近C++を全然書いていないもので・・。

鈴狼
記事: 2
登録日時: 5年前

Re: 添削をお願い致します

#3

投稿記事 by 鈴狼 » 5年前

様々なご指摘とご助言、ありがとうございます。

すごいと言って頂けて、光栄に思います。実はほとんどこの関数頼りで、音の解析に関する知識は全く無かったりします。
例題を弄繰り回しながら試行していた中、音楽と弾の生成タイミングがある程度マッチした今の形に落ち着きました。

>>もし明確にマッチできているのであれば、弾のショット音を付けると気持ちいのではないでしょうか。
>>速いショットで、音楽に合わせて弾が出ている様子が遊んでいてわかるような弾幕がいいのではないでしょうか。

確かに、本作品の特色を考えればその通りだと思います。早速試してみます。
龍神録2プログラミングの館の効果音も有難く使わせて頂きます。

私のC言語、C++言語の知識は全て龍神録プログラミングの館から学びました。
他の本や指南サイトを辞書的に読むことはあっても、他の書き方を知らないのでどうしても似通ってしまいます。

>>ただ、気になったのがprivateメンバ変数としてstatic変数があることです。
該当箇所と思われる部分を確認したところ、C言語で書かれた過去のコードからコピーしてきたものがそのままになっていることが原因でした。これは意図したものではなかったので、すぐに修正致しました(アンチパターンという単語は初めて見ました、これも勉強になります)。

変数名や関数名に対する指摘は大変勉強になりました。これから意識して名付けしていこうと思います。
メンバ変数を_から始めることは一般的なことだと思っていました。mですね、改めます。

TDD(テスト駆動開発)は文章を書くときの下書きのようなものだと理解しました。こちらも意識してみます。

>>せっかくTaskクラスを継承しているのにポリモーフィズムを利用していないところが多いのでオブジェクト指向のメリットがいかされていません。
>>また、Out(enemyshot_and_playerやplayershot_and_enemy)があるあたりオブジェクト指向の設計になっていません。

この二点については三日ほど時間を頂き、自分なりの考えをまとめてみました。

ポリモーフィズムをうまく使えてないなと感じている箇所は主にGameScene.cppです。
オブジェクト間のメンバ変数の参照や受け渡しの為に引数を指定しているのですが、そのままではエラーが発生してしまいました。関数のオーバーロードを利用してエラー回避は成りましたが、別々の関数と見做されて、ポリモーフィズムからは外れてしまって今に至ります。

引数指定を外して、受け渡し用の専用関数等を別に作った方が良いのでしょうか。
しかし、送り手と受け手に同じ変数(特に受け手の保管用)があるという状況に無駄を感じてしまってもいます。

Outクラスは自機と弾、敵機と弾の当たり判定を計算するものですが、オブジェクト自体を関数的に使うことがオブジェクト指向の設計から外れてしまっているという認識で間違いないでしょうか。そうだとすると、自機クラスと敵機クラスにOutクラスをそれぞれ継承してポリモーフィズムを利用するのが良い形になるのでしょうか。

ご紹介頂きました書籍も、近日中には図書館にでも探しに行って読んでみます。

長文になってしまいましたが、よろしくお願い致します。

アバター
Dixq (管理人)
管理人
記事: 1661
登録日時: 13年前
住所: 北海道札幌市
連絡を取る:

Re: 添削をお願い致します

#4

投稿記事 by Dixq (管理人) » 5年前

どれくらい正確に主旋律を取得できているか確認するため、弾の登録部分に短い効果音を入れてみました。

※限定公開動画ですが、削除した方がよければ削除します。

完璧かと言うとまだ改善の余地がありそうですね。
ただ、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日も考慮に入れる必要があります。
このような計算をしようと思ったらロジックがどんどん肥大化します。
このロジックをインスタンスの所有者の責務にするとどんどん負担が増えてしまうのです。

オブジェクト指向ではデータとロジックを同じクラスに実装します。

こういう話は私が先ほど紹介した本にも書いてあるのでじっくり読んでみてください。

> ご紹介頂きました書籍も、近日中には図書館にでも探しに行って読んでみます。

良いプログラミングの本って図書館にあるんでしょうか・・。
この辺の本は常に新しい本を読んだ方がいいので、大きな書店に行って端から読んで気に入ったものを買って読むのが良いかと思います。
もし金銭的な都合があるようであれば、キーワードさえわかればウェブでも学べます。
例えば「デザインパターン」とかでぐぐっただけでも、先人が体系化したよい設計のお手本が学べます。

アバター
Dixq (管理人)
管理人
記事: 1661
登録日時: 13年前
住所: 北海道札幌市
連絡を取る:

Re: 添削をお願い致します

#5

投稿記事 by Dixq (管理人) » 5年前

また、弾が90度に近い角度で飛んでいる時にウゴウゴしているように見えます。
1ドットずつピクピク動いてしまっています。
これは実数を使った位置に描画できる
DrawGraph"F"のF付関数を使ってfloatで座標を指定することと、
バイリニア補間を適用することで解決できます。
https://dixq.net/g/04_04.html
ここを参考にしてみてください。
弾が飛んでくるときピクピクせずにスーッと滑らかに表示されるようになります。

返信

“C言語何でも質問掲示板” へ戻る