7

SFML と OpenGL を使用して小さなゲームを作成することで、初心者の C++ スキルを練習しています。プログラミングの部分はおおむね順調に進んでいますが、実際のコード/クラスの設計について質問があります。

ゲーム ループを含み、イベント、グラフィックス、コマンド、ゲーム、および UI の各クラスのインスタンスを 1 つずつ所有する MainLoop という 1 つのクラスがあります。私は当初、それらすべてを 1 つのクラス (異なる .cpp ファイルに分割された関数を持つ) にしたいと考えていましたが、OOP/C++ のアプローチが間違っていると言われました。ただし、それらを分離することで良い面(カプセル化、モジュール性、デバッグ)を見ることができますが、多くの悪い面にも遭遇しているようです. ユーザーが UI ボタン​​を押す例を見てみましょう。

まず、MainLoop は SFML のウィンドウ クラスからイベントを取得します。MainLoop はそれを独自の Event クラスに送信します。イベント クラスはイベントを解釈し、それを UI クラスに送信して、ボタンのいずれかが「ヒット」したかどうかを確認します。true の場合、UI クラスは、ボタン コマンドを解釈する Command クラスにそれを送信します。そして最後に、コマンド クラスはそれを Game クラスまたはその他の必要な場所に送信します。

それはすべて私にとって非常に手間がかかるように思えます。また、少なくとも現在私が行っている方法では、多くの前方宣言が必要でした(そして、それらについて学ぶ前に、大量の循環依存関係が発生しました) . 私はそれがパフォーマンスに良い影響を与えるとは思えません。

とにかく、私が見逃しているトリックはありますか?これらのクラスをどのように接続し、どのように通信する必要がありますか? たとえば、Event クラスから UI クラスにコマンドを転送するにはどうすればよいですか? どこにでも前方宣言、インクルードなどを入れる必要がありますか?それはモジュール性を台無しにしませんか? MainLoop クラスをすべて実行し、代わりに宣言を必要としない整数/浮動小数点数/文字を使用して戻り値を転送する必要がありますか? 私はここでちょっと途方に暮れています。

4

2 に答える 2

4

ゲームの開発中に使用したデザインをいくつか提案できますが、確かに良いものではありませんが、多くの問題はありませんでした. アイデアを提供するために、簡単に説明します。

まず、ビュー マネージャーが必要です。このマネージャーは、ゲームの現在のビューを管理する必要があります。これは、ビューのスタックなどとして実装できます。したがってViewManager、ゲームのすべてのビューを認識し、現在のビューにディスパッチできるクラスが作成されます。

GameView次に、次のような外部からの基本的なインターフェイスを提供する抽象クラスが必要です。

  • drawMe()、ビューを描画します
  • receivedMouseEvent(Event e)、マウスイベントを受け取ります
  • activate()ビューマネージャーでビューがプッシュまたはポップされたときにdeactivate()実行する必要があるアクションを実行する

この抽象クラスを使用して、ゲームの特定のビューまたはビューの一部を実装して、それらをビュー スタックにプッシュおよびポップできるようにする必要があります。

良いことは、UI 要素を管理するサブクラスを用意することです。たとえばActiveArea、クリックに応答するクラスButtonを継承しActiveArea、2 状態のグラフィックを提供することもできます。これらの要素は、抽象的なビューに保存されているクリック可能な要素のリスト内に含まれている必要があります。これにより、すべての具体的なビューがそのボタンを共通の実装に安心して追加できます。このようにして、(メタコード)のようなものを持つことができます

void AbstractView::receiveEvent(Event e) {
  for (ActiveArea *area in areas)
    if (area.isInside(e)) {
      area->action();
      return;
    }

  innerReceiveEvent(e); //which should be a pure virtual function that will call a method specified in concrete views
}

このようにして、各ビューが独自の状態を管理し、ビュー マネージャーがイベントの描画と管理を担当します。

void ViewManager::draw() {
  for (AbstractView *view in views) // from top to bottom of the stack
    view.draw();
}
于 2012-12-02T18:26:04.230 に答える
3

重たいのは想像できますが、これが正しいやり方です。関数呼び出しはまったく重くなく、全体が読みやすくなっていることに注意してください。または、少なくともそうすべきです。;-)

すべてのクラスには、クラス定義を含むヘッダー ファイルが必要ですが、メンバー関数の実装は含まれません。任意のファイルに任意のクラス ヘッダー ファイルをインクルードできる必要があります。テンプレートを使用している場合にのみ(実装がヘッダーファイルにある必要があります)、循環依存関係が存在する可能性がありますが、あなたの説明から、それらがあるとは思いません。ヘッダーは互いに含める必要はありません。関数の引数で他のクラスへのポインターまたは参照を渡す必要がある場合は、ヘッダーの先頭で他のクラスを前方宣言しても問題ありません。ソースファイルの先頭に任意のインクルードを含めることができるはずです。そうでない場合は、あなたのケースでこれが必要であると考える理由について、より詳しい情報を提供してください。

于 2012-12-02T18:26:40.160 に答える